├── LICENSE ├── README.md ├── py_ecc ├── __init__.py ├── bn128 │ ├── __init__.py │ ├── bn128_curve.py │ ├── bn128_field_elements.py │ └── bn128_pairing.py ├── optimized_bn128 │ ├── __init__.py │ ├── optimized_curve.py │ ├── optimized_field_elements.py │ └── optimized_pairing.py └── secp256k1 │ ├── __init__.py │ └── secp256k1.py ├── setup.py └── tests ├── test_bn128.py └── test_secp256k1.py /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Vitalik Buterin 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archived Repository 2 | 3 | This repository implements optimal ate pairings over the bn_128 curve, but lacks recent updates. Developers seeking a more actively maintained alternative might consider [ethereum/py_ecc](https://github.com/ethereum/py_ecc), which offers similar functionalities with ongoing support. 4 | 5 | Related Resources: 6 | - [ethereum/py_ecc](https://github.com/ethereum/py_ecc): A Python library for elliptic curve cryptography, including bn_128 and bls12_381 curve operations. 7 | - [Ethereum for Python Developers](https://ethereum.org/en/developers/docs/programming-languages/python/): Comprehensive documentation and resources for Python developers in the Ethereum ecosystem. 8 | -------------------------------------------------------------------------------- /py_ecc/__init__.py: -------------------------------------------------------------------------------- 1 | from . import secp256k1 2 | from . import bn128 3 | from . import optimized_bn128 4 | -------------------------------------------------------------------------------- /py_ecc/bn128/__init__.py: -------------------------------------------------------------------------------- 1 | from .bn128_field_elements import field_modulus, FQ, FQP, FQ2, FQ12 2 | from .bn128_curve import add, double, multiply, is_inf, is_on_curve, eq, neg, twist, b, b2, b12, curve_order, G1, G2, G12 3 | from .bn128_pairing import pairing, final_exponentiate 4 | -------------------------------------------------------------------------------- /py_ecc/bn128/bn128_curve.py: -------------------------------------------------------------------------------- 1 | from .bn128_field_elements import field_modulus, FQ, FQ2, FQ12 2 | 3 | curve_order = 21888242871839275222246405745257275088548364400416034343698204186575808495617 4 | 5 | # Curve order should be prime 6 | assert pow(2, curve_order, curve_order) == 2 7 | # Curve order should be a factor of field_modulus**12 - 1 8 | assert (field_modulus ** 12 - 1) % curve_order == 0 9 | 10 | # Curve is y**2 = x**3 + 3 11 | b = FQ(3) 12 | # Twisted curve over FQ**2 13 | b2 = FQ2([3, 0]) / FQ2([9, 1]) 14 | # Extension curve over FQ**12; same b value as over FQ 15 | b12 = FQ12([3] + [0] * 11) 16 | 17 | # Generator for curve over FQ 18 | G1 = (FQ(1), FQ(2)) 19 | # Generator for twisted curve over FQ2 20 | G2 = (FQ2([10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634]), 21 | FQ2([8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531])) 22 | 23 | # Check if a point is the point at infinity 24 | def is_inf(pt): 25 | return pt is None 26 | 27 | # Check that a point is on the curve defined by y**2 == x**3 + b 28 | def is_on_curve(pt, b): 29 | if is_inf(pt): 30 | return True 31 | x, y = pt 32 | return y**2 - x**3 == b 33 | 34 | assert is_on_curve(G1, b) 35 | assert is_on_curve(G2, b2) 36 | 37 | # Elliptic curve doubling 38 | def double(pt): 39 | x, y = pt 40 | l = 3 * x**2 / (2 * y) 41 | newx = l**2 - 2 * x 42 | newy = -l * newx + l * x - y 43 | return newx, newy 44 | 45 | # Elliptic curve addition 46 | def add(p1, p2): 47 | if p1 is None or p2 is None: 48 | return p1 if p2 is None else p2 49 | x1, y1 = p1 50 | x2, y2 = p2 51 | if x2 == x1 and y2 == y1: 52 | return double(p1) 53 | elif x2 == x1: 54 | return None 55 | else: 56 | l = (y2 - y1) / (x2 - x1) 57 | newx = l**2 - x1 - x2 58 | newy = -l * newx + l * x1 - y1 59 | assert newy == (-l * newx + l * x2 - y2) 60 | return (newx, newy) 61 | 62 | # Elliptic curve point multiplication 63 | def multiply(pt, n): 64 | if n == 0: 65 | return None 66 | elif n == 1: 67 | return pt 68 | elif not n % 2: 69 | return multiply(double(pt), n // 2) 70 | else: 71 | return add(multiply(double(pt), int(n // 2)), pt) 72 | 73 | def eq(p1, p2): 74 | return p1 == p2 75 | 76 | # "Twist" a point in E(FQ2) into a point in E(FQ12) 77 | w = FQ12([0, 1] + [0] * 10) 78 | 79 | # Convert P => -P 80 | def neg(pt): 81 | if pt is None: 82 | return None 83 | x, y = pt 84 | return (x, -y) 85 | 86 | def twist(pt): 87 | if pt is None: 88 | return None 89 | _x, _y = pt 90 | # Field isomorphism from Z[p] / x**2 to Z[p] / x**2 - 18*x + 82 91 | xcoeffs = [_x.coeffs[0] - _x.coeffs[1] * 9, _x.coeffs[1]] 92 | ycoeffs = [_y.coeffs[0] - _y.coeffs[1] * 9, _y.coeffs[1]] 93 | # Isomorphism into subfield of Z[p] / w**12 - 18 * w**6 + 82, 94 | # where w**6 = x 95 | nx = FQ12([xcoeffs[0]] + [0] * 5 + [xcoeffs[1]] + [0] * 5) 96 | ny = FQ12([ycoeffs[0]] + [0] * 5 + [ycoeffs[1]] + [0] * 5) 97 | # Divide x coord by w**2 and y coord by w**3 98 | return (nx * w **2, ny * w**3) 99 | 100 | G12 = twist(G2) 101 | # Check that the twist creates a point that is on the curve 102 | assert is_on_curve(G12, b12) 103 | -------------------------------------------------------------------------------- /py_ecc/bn128/bn128_field_elements.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.setrecursionlimit(10000) 3 | 4 | # python3 compatibility 5 | try: 6 | foo = long 7 | except: 8 | long = int 9 | 10 | # The prime modulus of the field 11 | field_modulus = 21888242871839275222246405745257275088696311157297823662689037894645226208583 12 | # See, it's prime! 13 | assert pow(2, field_modulus, field_modulus) == 2 14 | 15 | # The modulus of the polynomial in this representation of FQ12 16 | FQ12_modulus_coeffs = [82, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0] # Implied + [1] 17 | 18 | # Extended euclidean algorithm to find modular inverses for 19 | # integers 20 | def inv(a, n): 21 | if a == 0: 22 | return 0 23 | lm, hm = 1, 0 24 | low, high = a % n, n 25 | while low > 1: 26 | r = high//low 27 | nm, new = hm-lm*r, high-low*r 28 | lm, low, hm, high = nm, new, lm, low 29 | return lm % n 30 | 31 | # A class for field elements in FQ. Wrap a number in this class, 32 | # and it becomes a field element. 33 | class FQ(): 34 | def __init__(self, n): 35 | if isinstance(n, self.__class__): 36 | self.n = n.n 37 | else: 38 | self.n = n % field_modulus 39 | assert isinstance(self.n, (int, long)) 40 | 41 | def __add__(self, other): 42 | on = other.n if isinstance(other, FQ) else other 43 | return FQ((self.n + on) % field_modulus) 44 | 45 | def __mul__(self, other): 46 | on = other.n if isinstance(other, FQ) else other 47 | return FQ((self.n * on) % field_modulus) 48 | 49 | def __rmul__(self, other): 50 | return self * other 51 | 52 | def __radd__(self, other): 53 | return self + other 54 | 55 | def __rsub__(self, other): 56 | on = other.n if isinstance(other, FQ) else other 57 | return FQ((on - self.n) % field_modulus) 58 | 59 | def __sub__(self, other): 60 | on = other.n if isinstance(other, FQ) else other 61 | return FQ((self.n - on) % field_modulus) 62 | 63 | def __div__(self, other): 64 | on = other.n if isinstance(other, FQ) else other 65 | assert isinstance(on, (int, long)) 66 | return FQ(self.n * inv(on, field_modulus) % field_modulus) 67 | 68 | def __truediv__(self, other): 69 | return self.__div__(other) 70 | 71 | def __rdiv__(self, other): 72 | on = other.n if isinstance(other, FQ) else other 73 | assert isinstance(on, (int, long)), on 74 | return FQ(inv(self.n, field_modulus) * on % field_modulus) 75 | 76 | def __rtruediv__(self, other): 77 | return self.__rdiv__(other) 78 | 79 | def __pow__(self, other): 80 | if other == 0: 81 | return FQ(1) 82 | elif other == 1: 83 | return FQ(self.n) 84 | elif other % 2 == 0: 85 | return (self * self) ** (other // 2) 86 | else: 87 | return ((self * self) ** int(other // 2)) * self 88 | 89 | def __eq__(self, other): 90 | if isinstance(other, FQ): 91 | return self.n == other.n 92 | else: 93 | return self.n == other 94 | 95 | def __ne__(self, other): 96 | return not self == other 97 | 98 | def __neg__(self): 99 | return FQ(-self.n) 100 | 101 | def __repr__(self): 102 | return repr(self.n) 103 | 104 | @classmethod 105 | def one(cls): 106 | return cls(1) 107 | 108 | @classmethod 109 | def zero(cls): 110 | return cls(0) 111 | 112 | # Utility methods for polynomial math 113 | def deg(p): 114 | d = len(p) - 1 115 | while p[d] == 0 and d: 116 | d -= 1 117 | return d 118 | 119 | def poly_rounded_div(a, b): 120 | dega = deg(a) 121 | degb = deg(b) 122 | temp = [x for x in a] 123 | o = [0 for x in a] 124 | for i in range(dega - degb, -1, -1): 125 | o[i] += temp[degb + i] / b[degb] 126 | for c in range(degb + 1): 127 | temp[c + i] -= o[c] 128 | return o[:deg(o)+1] 129 | 130 | # A class for elements in polynomial extension fields 131 | class FQP(): 132 | def __init__(self, coeffs, modulus_coeffs): 133 | assert len(coeffs) == len(modulus_coeffs) 134 | self.coeffs = [FQ(c) for c in coeffs] 135 | # The coefficients of the modulus, without the leading [1] 136 | self.modulus_coeffs = modulus_coeffs 137 | # The degree of the extension field 138 | self.degree = len(self.modulus_coeffs) 139 | 140 | def __add__(self, other): 141 | assert isinstance(other, self.__class__) 142 | return self.__class__([x+y for x,y in zip(self.coeffs, other.coeffs)]) 143 | 144 | def __sub__(self, other): 145 | assert isinstance(other, self.__class__) 146 | return self.__class__([x-y for x,y in zip(self.coeffs, other.coeffs)]) 147 | 148 | def __mul__(self, other): 149 | if isinstance(other, (FQ, int, long)): 150 | return self.__class__([c * other for c in self.coeffs]) 151 | else: 152 | assert isinstance(other, self.__class__) 153 | b = [FQ(0) for i in range(self.degree * 2 - 1)] 154 | for i in range(self.degree): 155 | for j in range(self.degree): 156 | b[i + j] += self.coeffs[i] * other.coeffs[j] 157 | while len(b) > self.degree: 158 | exp, top = len(b) - self.degree - 1, b.pop() 159 | for i in range(self.degree): 160 | b[exp + i] -= top * FQ(self.modulus_coeffs[i]) 161 | return self.__class__(b) 162 | 163 | def __rmul__(self, other): 164 | return self * other 165 | 166 | def __div__(self, other): 167 | if isinstance(other, (FQ, int, long)): 168 | return self.__class__([c / other for c in self.coeffs]) 169 | else: 170 | assert isinstance(other, self.__class__) 171 | return self * other.inv() 172 | 173 | def __truediv__(self, other): 174 | return self.__div__(other) 175 | 176 | def __pow__(self, other): 177 | if other == 0: 178 | return self.__class__([1] + [0] * (self.degree - 1)) 179 | elif other == 1: 180 | return self.__class__(self.coeffs) 181 | elif other % 2 == 0: 182 | return (self * self) ** (other // 2) 183 | else: 184 | return ((self * self) ** int(other // 2)) * self 185 | 186 | # Extended euclidean algorithm used to find the modular inverse 187 | def inv(self): 188 | lm, hm = [1] + [0] * self.degree, [0] * (self.degree + 1) 189 | low, high = self.coeffs + [0], self.modulus_coeffs + [1] 190 | while deg(low): 191 | r = poly_rounded_div(high, low) 192 | r += [0] * (self.degree + 1 - len(r)) 193 | nm = [x for x in hm] 194 | new = [x for x in high] 195 | assert len(lm) == len(hm) == len(low) == len(high) == len(nm) == len(new) == self.degree + 1 196 | for i in range(self.degree + 1): 197 | for j in range(self.degree + 1 - i): 198 | nm[i+j] -= lm[i] * r[j] 199 | new[i+j] -= low[i] * r[j] 200 | lm, low, hm, high = nm, new, lm, low 201 | return self.__class__(lm[:self.degree]) / low[0] 202 | 203 | def __repr__(self): 204 | return repr(self.coeffs) 205 | 206 | def __eq__(self, other): 207 | assert isinstance(other, self.__class__) 208 | for c1, c2 in zip(self.coeffs, other.coeffs): 209 | if c1 != c2: 210 | return False 211 | return True 212 | 213 | def __ne__(self, other): 214 | return not self == other 215 | 216 | def __neg__(self): 217 | return self.__class__([-c for c in self.coeffs]) 218 | 219 | @classmethod 220 | def one(cls): 221 | return cls([1] + [0] * (cls.degree - 1)) 222 | 223 | @classmethod 224 | def zero(cls): 225 | return cls([0] * cls.degree) 226 | 227 | # The quadratic extension field 228 | class FQ2(FQP): 229 | def __init__(self, coeffs): 230 | self.coeffs = [FQ(c) for c in coeffs] 231 | self.modulus_coeffs = [1, 0] 232 | self.degree = 2 233 | self.__class__.degree = 2 234 | 235 | # The 12th-degree extension field 236 | class FQ12(FQP): 237 | def __init__(self, coeffs): 238 | self.coeffs = [FQ(c) for c in coeffs] 239 | self.modulus_coeffs = FQ12_modulus_coeffs 240 | self.degree = 12 241 | self.__class__.degree = 12 242 | -------------------------------------------------------------------------------- /py_ecc/bn128/bn128_pairing.py: -------------------------------------------------------------------------------- 1 | from .bn128_curve import double, add, multiply, is_on_curve, neg, twist, b, b2, b12, curve_order, G1, G2, G12 2 | from .bn128_field_elements import field_modulus, FQ, FQ2, FQ12 3 | 4 | ate_loop_count = 29793968203157093288 5 | log_ate_loop_count = 63 6 | 7 | # Create a function representing the line between P1 and P2, 8 | # and evaluate it at T 9 | def linefunc(P1, P2, T): 10 | assert P1 and P2 and T # No points-at-infinity allowed, sorry 11 | x1, y1 = P1 12 | x2, y2 = P2 13 | xt, yt = T 14 | if x1 != x2: 15 | m = (y2 - y1) / (x2 - x1) 16 | return m * (xt - x1) - (yt - y1) 17 | elif y1 == y2: 18 | m = 3 * x1**2 / (2 * y1) 19 | return m * (xt - x1) - (yt - y1) 20 | else: 21 | return xt - x1 22 | 23 | def cast_point_to_fq12(pt): 24 | if pt is None: 25 | return None 26 | x, y = pt 27 | return (FQ12([x.n] + [0] * 11), FQ12([y.n] + [0] * 11)) 28 | 29 | # Check consistency of the "line function" 30 | one, two, three = G1, double(G1), multiply(G1, 3) 31 | negone, negtwo, negthree = multiply(G1, curve_order - 1), multiply(G1, curve_order - 2), multiply(G1, curve_order - 3) 32 | 33 | assert linefunc(one, two, one) == FQ(0) 34 | assert linefunc(one, two, two) == FQ(0) 35 | assert linefunc(one, two, three) != FQ(0) 36 | assert linefunc(one, two, negthree) == FQ(0) 37 | assert linefunc(one, negone, one) == FQ(0) 38 | assert linefunc(one, negone, negone) == FQ(0) 39 | assert linefunc(one, negone, two) != FQ(0) 40 | assert linefunc(one, one, one) == FQ(0) 41 | assert linefunc(one, one, two) != FQ(0) 42 | assert linefunc(one, one, negtwo) == FQ(0) 43 | 44 | # Main miller loop 45 | def miller_loop(Q, P): 46 | if Q is None or P is None: 47 | return FQ12.one() 48 | R = Q 49 | f = FQ12.one() 50 | for i in range(log_ate_loop_count, -1, -1): 51 | f = f * f * linefunc(R, R, P) 52 | R = double(R) 53 | if ate_loop_count & (2**i): 54 | f = f * linefunc(R, Q, P) 55 | R = add(R, Q) 56 | # assert R == multiply(Q, ate_loop_count) 57 | Q1 = (Q[0] ** field_modulus, Q[1] ** field_modulus) 58 | # assert is_on_curve(Q1, b12) 59 | nQ2 = (Q1[0] ** field_modulus, -Q1[1] ** field_modulus) 60 | # assert is_on_curve(nQ2, b12) 61 | f = f * linefunc(R, Q1, P) 62 | R = add(R, Q1) 63 | f = f * linefunc(R, nQ2, P) 64 | # R = add(R, nQ2) This line is in many specifications but it technically does nothing 65 | return f ** ((field_modulus ** 12 - 1) // curve_order) 66 | 67 | # Pairing computation 68 | def pairing(Q, P): 69 | assert is_on_curve(Q, b2) 70 | assert is_on_curve(P, b) 71 | return miller_loop(twist(Q), cast_point_to_fq12(P)) 72 | 73 | def final_exponentiate(p): 74 | return p ** ((field_modulus ** 12 - 1) // curve_order) 75 | -------------------------------------------------------------------------------- /py_ecc/optimized_bn128/__init__.py: -------------------------------------------------------------------------------- 1 | from .optimized_field_elements import field_modulus, FQ, FQP, FQ2, FQ12 2 | from .optimized_curve import add, double, multiply, is_inf, is_on_curve, eq, neg, twist, b, b2, b12, curve_order, G1, G2, G12, normalize 3 | from .optimized_pairing import pairing, final_exponentiate 4 | -------------------------------------------------------------------------------- /py_ecc/optimized_bn128/optimized_curve.py: -------------------------------------------------------------------------------- 1 | from .optimized_field_elements import FQ2, FQ12, field_modulus, FQ 2 | 3 | curve_order = 21888242871839275222246405745257275088548364400416034343698204186575808495617 4 | 5 | # Curve order should be prime 6 | assert pow(2, curve_order, curve_order) == 2 7 | # Curve order should be a factor of field_modulus**12 - 1 8 | assert (field_modulus ** 12 - 1) % curve_order == 0 9 | 10 | # Curve is y**2 = x**3 + 3 11 | b = FQ(3) 12 | # Twisted curve over FQ**2 13 | b2 = FQ2([3, 0]) / FQ2([9, 1]) 14 | # Extension curve over FQ**12; same b value as over FQ 15 | b12 = FQ12([3] + [0] * 11) 16 | 17 | # Generator for curve over FQ 18 | G1 = (FQ(1), FQ(2), FQ(1)) 19 | # Generator for twisted curve over FQ2 20 | G2 = (FQ2([10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634]), 21 | FQ2([8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531]), FQ2.one()) 22 | 23 | # Check if a point is the point at infinity 24 | def is_inf(pt): 25 | return pt[-1] == pt[-1].__class__.zero() 26 | 27 | # Check that a point is on the curve defined by y**2 == x**3 + b 28 | def is_on_curve(pt, b): 29 | if is_inf(pt): 30 | return True 31 | x, y, z = pt 32 | return y**2 * z - x**3 == b * z**3 33 | 34 | assert is_on_curve(G1, b) 35 | assert is_on_curve(G2, b2) 36 | 37 | # Elliptic curve doubling 38 | def double(pt): 39 | x, y, z = pt 40 | W = 3 * x * x 41 | S = y * z 42 | B = x * y * S 43 | H = W * W - 8 * B 44 | S_squared = S * S 45 | newx = 2 * H * S 46 | newy = W * (4 * B - H) - 8 * y * y * S_squared 47 | newz = 8 * S * S_squared 48 | return newx, newy, newz 49 | 50 | # Elliptic curve addition 51 | def add(p1, p2): 52 | one, zero = p1[0].__class__.one(), p1[0].__class__.zero() 53 | if p1[2] == zero or p2[2] == zero: 54 | return p1 if p2[2] == zero else p2 55 | x1, y1, z1 = p1 56 | x2, y2, z2 = p2 57 | U1 = y2 * z1 58 | U2 = y1 * z2 59 | V1 = x2 * z1 60 | V2 = x1 * z2 61 | if V1 == V2 and U1 == U2: 62 | return double(p1) 63 | elif V1 == V2: 64 | return (one, one, zero) 65 | U = U1 - U2 66 | V = V1 - V2 67 | V_squared = V * V 68 | V_squared_times_V2 = V_squared * V2 69 | V_cubed = V * V_squared 70 | W = z1 * z2 71 | A = U * U * W - V_cubed - 2 * V_squared_times_V2 72 | newx = V * A 73 | newy = U * (V_squared_times_V2 - A) - V_cubed * U2 74 | newz = V_cubed * W 75 | return (newx, newy, newz) 76 | 77 | # Elliptic curve point multiplication 78 | def multiply(pt, n): 79 | if n == 0: 80 | return (pt[0].__class__.one(), pt[0].__class__.one(), pt[0].__class__.zero()) 81 | elif n == 1: 82 | return pt 83 | elif not n % 2: 84 | return multiply(double(pt), n // 2) 85 | else: 86 | return add(multiply(double(pt), int(n // 2)), pt) 87 | 88 | def eq(p1, p2): 89 | x1, y1, z1 = p1 90 | x2, y2, z2 = p2 91 | return x1 * z2 == x2 * z1 and y1 * z2 == y2 * z1 92 | 93 | def normalize(pt): 94 | x, y, z = pt 95 | return (x / z, y / z) 96 | 97 | # "Twist" a point in E(FQ2) into a point in E(FQ12) 98 | w = FQ12([0, 1] + [0] * 10) 99 | 100 | # Convert P => -P 101 | def neg(pt): 102 | if pt is None: 103 | return None 104 | x, y, z = pt 105 | return (x, -y, z) 106 | 107 | def twist(pt): 108 | if pt is None: 109 | return None 110 | _x, _y, _z = pt 111 | # Field isomorphism from Z[p] / x**2 to Z[p] / x**2 - 18*x + 82 112 | xcoeffs = [_x.coeffs[0] - _x.coeffs[1] * 9, _x.coeffs[1]] 113 | ycoeffs = [_y.coeffs[0] - _y.coeffs[1] * 9, _y.coeffs[1]] 114 | zcoeffs = [_z.coeffs[0] - _z.coeffs[1] * 9, _z.coeffs[1]] 115 | x, y, z = _x - _y * 9, _y, _z 116 | nx = FQ12([xcoeffs[0]] + [0] * 5 + [xcoeffs[1]] + [0] * 5) 117 | ny = FQ12([ycoeffs[0]] + [0] * 5 + [ycoeffs[1]] + [0] * 5) 118 | nz = FQ12([zcoeffs[0]] + [0] * 5 + [zcoeffs[1]] + [0] * 5) 119 | return (nx * w **2, ny * w**3, nz) 120 | 121 | # Check that the twist creates a point that is on the curve 122 | G12 = twist(G2) 123 | assert is_on_curve(G12, b12) 124 | -------------------------------------------------------------------------------- /py_ecc/optimized_bn128/optimized_field_elements.py: -------------------------------------------------------------------------------- 1 | field_modulus = 21888242871839275222246405745257275088696311157297823662689037894645226208583 2 | FQ12_modulus_coeffs = [82, 0, 0, 0, 0, 0, -18, 0, 0, 0, 0, 0] # Implied + [1] 3 | FQ12_mc_tuples = [(i, c) for i, c in enumerate(FQ12_modulus_coeffs) if c] 4 | 5 | # python3 compatibility 6 | try: 7 | foo = long 8 | except: 9 | long = int 10 | 11 | # Extended euclidean algorithm to find modular inverses for 12 | # integers 13 | def prime_field_inv(a, n): 14 | if a == 0: 15 | return 0 16 | lm, hm = 1, 0 17 | low, high = a % n, n 18 | while low > 1: 19 | r = high//low 20 | nm, new = hm-lm*r, high-low*r 21 | lm, low, hm, high = nm, new, lm, low 22 | return lm % n 23 | 24 | # A class for field elements in FQ. Wrap a number in this class, 25 | # and it becomes a field element. 26 | class FQ(): 27 | def __init__(self, n): 28 | if isinstance(n, self.__class__): 29 | self.n = n.n 30 | else: 31 | self.n = n % field_modulus 32 | assert isinstance(self.n, (int, long)) 33 | 34 | def __add__(self, other): 35 | on = other.n if isinstance(other, FQ) else other 36 | return FQ((self.n + on) % field_modulus) 37 | 38 | def __mul__(self, other): 39 | on = other.n if isinstance(other, FQ) else other 40 | return FQ((self.n * on) % field_modulus) 41 | 42 | def __rmul__(self, other): 43 | return self * other 44 | 45 | def __radd__(self, other): 46 | return self + other 47 | 48 | def __rsub__(self, other): 49 | on = other.n if isinstance(other, FQ) else other 50 | return FQ((on - self.n) % field_modulus) 51 | 52 | def __sub__(self, other): 53 | on = other.n if isinstance(other, FQ) else other 54 | return FQ((self.n - on) % field_modulus) 55 | 56 | def __div__(self, other): 57 | on = other.n if isinstance(other, FQ) else other 58 | assert isinstance(on, (int, long)) 59 | return FQ(self.n * prime_field_inv(on, field_modulus) % field_modulus) 60 | 61 | def __truediv__(self, other): 62 | return self.__div__(other) 63 | 64 | def __rdiv__(self, other): 65 | on = other.n if isinstance(other, FQ) else other 66 | assert isinstance(on, (int, long)), on 67 | return FQ(prime_field_inv(self.n, field_modulus) * on % field_modulus) 68 | 69 | def __rtruediv__(self, other): 70 | return self.__rdiv__(other) 71 | 72 | def __pow__(self, other): 73 | if other == 0: 74 | return FQ(1) 75 | elif other == 1: 76 | return FQ(self.n) 77 | elif other % 2 == 0: 78 | return (self * self) ** (other // 2) 79 | else: 80 | return ((self * self) ** int(other // 2)) * self 81 | 82 | def __eq__(self, other): 83 | if isinstance(other, FQ): 84 | return self.n == other.n 85 | else: 86 | return self.n == other 87 | 88 | def __ne__(self, other): 89 | return not self == other 90 | 91 | def __neg__(self): 92 | return FQ(-self.n) 93 | 94 | def __repr__(self): 95 | return repr(self.n) 96 | 97 | @classmethod 98 | def one(cls): 99 | return cls(1) 100 | 101 | @classmethod 102 | def zero(cls): 103 | return cls(0) 104 | 105 | # Utility methods for polynomial math 106 | def deg(p): 107 | d = len(p) - 1 108 | while p[d] == 0 and d: 109 | d -= 1 110 | return d 111 | 112 | def poly_rounded_div(a, b): 113 | dega = deg(a) 114 | degb = deg(b) 115 | temp = [x for x in a] 116 | o = [0 for x in a] 117 | for i in range(dega - degb, -1, -1): 118 | o[i] = (o[i] + temp[degb + i] * prime_field_inv(b[degb], field_modulus)) 119 | for c in range(degb + 1): 120 | temp[c + i] = (temp[c + i] - o[c]) 121 | return [x % field_modulus for x in o[:deg(o)+1]] 122 | 123 | # A class for elements in polynomial extension fields 124 | class FQP(): 125 | def __init__(self, coeffs, modulus_coeffs): 126 | assert len(coeffs) == len(modulus_coeffs) 127 | self.coeffs = coeffs 128 | # The coefficients of the modulus, without the leading [1] 129 | self.modulus_coeffs = modulus_coeffs 130 | # The degree of the extension field 131 | self.degree = len(self.modulus_coeffs) 132 | 133 | def __add__(self, other): 134 | assert isinstance(other, self.__class__) 135 | return self.__class__([(x+y) % field_modulus for x,y in zip(self.coeffs, other.coeffs)]) 136 | 137 | def __sub__(self, other): 138 | assert isinstance(other, self.__class__) 139 | return self.__class__([(x-y) % field_modulus for x,y in zip(self.coeffs, other.coeffs)]) 140 | 141 | def __mul__(self, other): 142 | if isinstance(other, (int, long)): 143 | return self.__class__([c * other % field_modulus for c in self.coeffs]) 144 | else: 145 | # assert isinstance(other, self.__class__) 146 | b = [0] * (self.degree * 2 - 1) 147 | inner_enumerate = list(enumerate(other.coeffs)) 148 | for i, eli in enumerate(self.coeffs): 149 | for j, elj in inner_enumerate: 150 | b[i + j] += eli * elj 151 | # MID = len(self.coeffs) // 2 152 | for exp in range(self.degree - 2, -1, -1): 153 | top = b.pop() 154 | for i, c in self.mc_tuples: 155 | b[exp + i] -= top * c 156 | return self.__class__([x % field_modulus for x in b]) 157 | 158 | def __rmul__(self, other): 159 | return self * other 160 | 161 | def __div__(self, other): 162 | if isinstance(other, (int, long)): 163 | return self.__class__([c * prime_field_inv(other, field_modulus) % field_modulus for c in self.coeffs]) 164 | else: 165 | assert isinstance(other, self.__class__) 166 | return self * other.inv() 167 | 168 | def __truediv__(self, other): 169 | return self.__div__(other) 170 | 171 | def __pow__(self, other): 172 | o = self.__class__([1] + [0] * (self.degree - 1)) 173 | t = self 174 | while other > 0: 175 | if other & 1: 176 | o = o * t 177 | other >>= 1 178 | t = t * t 179 | return o 180 | 181 | # Extended euclidean algorithm used to find the modular inverse 182 | def inv(self): 183 | lm, hm = [1] + [0] * self.degree, [0] * (self.degree + 1) 184 | low, high = self.coeffs + [0], self.modulus_coeffs + [1] 185 | while deg(low): 186 | r = poly_rounded_div(high, low) 187 | r += [0] * (self.degree + 1 - len(r)) 188 | nm = [x for x in hm] 189 | new = [x for x in high] 190 | # assert len(lm) == len(hm) == len(low) == len(high) == len(nm) == len(new) == self.degree + 1 191 | for i in range(self.degree + 1): 192 | for j in range(self.degree + 1 - i): 193 | nm[i+j] -= lm[i] * r[j] 194 | new[i+j] -= low[i] * r[j] 195 | nm = [x % field_modulus for x in nm] 196 | new = [x % field_modulus for x in new] 197 | lm, low, hm, high = nm, new, lm, low 198 | return self.__class__(lm[:self.degree]) / low[0] 199 | 200 | def __repr__(self): 201 | return repr(self.coeffs) 202 | 203 | def __eq__(self, other): 204 | assert isinstance(other, self.__class__) 205 | for c1, c2 in zip(self.coeffs, other.coeffs): 206 | if c1 != c2: 207 | return False 208 | return True 209 | 210 | def __ne__(self, other): 211 | return not self == other 212 | 213 | def __neg__(self): 214 | return self.__class__([-c for c in self.coeffs]) 215 | 216 | @classmethod 217 | def one(cls): 218 | return cls([1] + [0] * (cls.degree - 1)) 219 | 220 | @classmethod 221 | def zero(cls): 222 | return cls([0] * cls.degree) 223 | 224 | # The quadratic extension field 225 | class FQ2(FQP): 226 | def __init__(self, coeffs): 227 | self.coeffs = coeffs 228 | self.modulus_coeffs = [1, 0] 229 | self.mc_tuples = [(0, 1)] 230 | self.degree = 2 231 | self.__class__.degree = 2 232 | 233 | # The 12th-degree extension field 234 | class FQ12(FQP): 235 | def __init__(self, coeffs): 236 | self.coeffs = coeffs 237 | self.modulus_coeffs = FQ12_modulus_coeffs 238 | self.mc_tuples = FQ12_mc_tuples 239 | self.degree = 12 240 | self.__class__.degree = 12 241 | -------------------------------------------------------------------------------- /py_ecc/optimized_bn128/optimized_pairing.py: -------------------------------------------------------------------------------- 1 | from .optimized_curve import double, add, multiply, is_on_curve, neg, twist, b, b2, b12, curve_order, G1, G2, G12, normalize 2 | from .optimized_field_elements import FQ2, FQ12, field_modulus, FQ 3 | 4 | ate_loop_count = 29793968203157093288 5 | log_ate_loop_count = 63 6 | pseudo_binary_encoding = [0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, 7 | 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1, 8 | 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 9 | 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1] 10 | 11 | assert sum([e * 2**i for i, e in enumerate(pseudo_binary_encoding)]) == ate_loop_count 12 | 13 | 14 | def normalize1(p): 15 | x, y = normalize(p) 16 | return x, y, x.__class__.one() 17 | 18 | # Create a function representing the line between P1 and P2, 19 | # and evaluate it at T. Returns a numerator and a denominator 20 | # to avoid unneeded divisions 21 | def linefunc(P1, P2, T): 22 | zero = P1[0].__class__.zero() 23 | x1, y1, z1 = P1 24 | x2, y2, z2 = P2 25 | xt, yt, zt = T 26 | # points in projective coords: (x / z, y / z) 27 | # hence, m = (y2/z2 - y1/z1) / (x2/z2 - x1/z1) 28 | # multiply numerator and denominator by z1z2 to get values below 29 | m_numerator = y2 * z1 - y1 * z2 30 | m_denominator = x2 * z1 - x1 * z2 31 | if m_denominator != zero: 32 | # m * ((xt/zt) - (x1/z1)) - ((yt/zt) - (y1/z1)) 33 | return m_numerator * (xt * z1 - x1 * zt) - m_denominator * (yt * z1 - y1 * zt), \ 34 | m_denominator * zt * z1 35 | elif m_numerator == zero: 36 | # m = 3(x/z)^2 / 2(y/z), multiply num and den by z**2 37 | m_numerator = 3 * x1 * x1 38 | m_denominator = 2 * y1 * z1 39 | return m_numerator * (xt * z1 - x1 * zt) - m_denominator * (yt * z1 - y1 * zt), \ 40 | m_denominator * zt * z1 41 | else: 42 | return xt * z1 - x1 * zt, z1 * zt 43 | 44 | def cast_point_to_fq12(pt): 45 | if pt is None: 46 | return None 47 | x, y, z = pt 48 | return (FQ12([x.n] + [0] * 11), FQ12([y.n] + [0] * 11), FQ12([z.n] + [0] * 11)) 49 | 50 | # Check consistency of the "line function" 51 | one, two, three = G1, double(G1), multiply(G1, 3) 52 | negone, negtwo, negthree = multiply(G1, curve_order - 1), multiply(G1, curve_order - 2), multiply(G1, curve_order - 3) 53 | 54 | assert linefunc(one, two, one)[0] == FQ(0) 55 | assert linefunc(one, two, two)[0] == FQ(0) 56 | assert linefunc(one, two, three)[0] != FQ(0) 57 | assert linefunc(one, two, negthree)[0] == FQ(0) 58 | assert linefunc(one, negone, one)[0] == FQ(0) 59 | assert linefunc(one, negone, negone)[0] == FQ(0) 60 | assert linefunc(one, negone, two)[0] != FQ(0) 61 | assert linefunc(one, one, one)[0] == FQ(0) 62 | assert linefunc(one, one, two)[0] != FQ(0) 63 | assert linefunc(one, one, negtwo)[0] == FQ(0) 64 | 65 | # Main miller loop 66 | def miller_loop(Q, P, final_exponentiate=True): 67 | if Q is None or P is None: 68 | return FQ12.one() 69 | R = Q 70 | f_num, f_den = FQ12.one(), FQ12.one() 71 | for b in pseudo_binary_encoding[63::-1]: 72 | #for i in range(log_ate_loop_count, -1, -1): 73 | _n, _d = linefunc(R, R, P) 74 | f_num = f_num * f_num * _n 75 | f_den = f_den * f_den * _d 76 | R = double(R) 77 | #if ate_loop_count & (2**i): 78 | if b == 1: 79 | _n, _d = linefunc(R, Q, P) 80 | f_num = f_num * _n 81 | f_den = f_den * _d 82 | R = add(R, Q) 83 | elif b == -1: 84 | nQ = neg(Q) 85 | _n, _d = linefunc(R, nQ, P) 86 | f_num = f_num * _n 87 | f_den = f_den * _d 88 | R = add(R, nQ) 89 | # assert R == multiply(Q, ate_loop_count) 90 | Q1 = (Q[0] ** field_modulus, Q[1] ** field_modulus, Q[2] ** field_modulus) 91 | # assert is_on_curve(Q1, b12) 92 | nQ2 = (Q1[0] ** field_modulus, -Q1[1] ** field_modulus, Q1[2] ** field_modulus) 93 | # assert is_on_curve(nQ2, b12) 94 | _n1, _d1 = linefunc(R, Q1, P) 95 | R = add(R, Q1) 96 | _n2, _d2 = linefunc(R, nQ2, P) 97 | f = f_num * _n1 * _n2 / (f_den * _d1 * _d2) 98 | # R = add(R, nQ2) This line is in many specifications but it technically does nothing 99 | if final_exponentiate: 100 | return f ** ((field_modulus ** 12 - 1) // curve_order) 101 | else: 102 | return f 103 | 104 | # Pairing computation 105 | def pairing(Q, P, final_exponentiate=True): 106 | assert is_on_curve(Q, b2) 107 | assert is_on_curve(P, b) 108 | if P[-1] == P[-1].__class__.zero() or Q[-1] == Q[-1].__class__.zero(): 109 | return FQ12.one() 110 | return miller_loop(twist(Q), cast_point_to_fq12(P), final_exponentiate=final_exponentiate) 111 | 112 | def final_exponentiate(p): 113 | return p ** ((field_modulus ** 12 - 1) // curve_order) 114 | -------------------------------------------------------------------------------- /py_ecc/secp256k1/__init__.py: -------------------------------------------------------------------------------- 1 | from .secp256k1 import privtopub, ecdsa_raw_sign, ecdsa_raw_recover, N, P, G 2 | -------------------------------------------------------------------------------- /py_ecc/secp256k1/secp256k1.py: -------------------------------------------------------------------------------- 1 | import hashlib, hmac 2 | import sys 3 | if sys.version[0] == '2': 4 | safe_ord = ord 5 | else: 6 | safe_ord = lambda x: x 7 | 8 | # Elliptic curve parameters (secp256k1) 9 | 10 | P = 2**256 - 2**32 - 977 11 | N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 12 | A = 0 13 | B = 7 14 | Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240 15 | Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 16 | G = (Gx, Gy) 17 | 18 | def bytes_to_int(x): 19 | o = 0 20 | for b in x: 21 | o = (o << 8) + safe_ord(b) 22 | return o 23 | 24 | # Extended Euclidean Algorithm 25 | def inv(a, n): 26 | if a == 0: 27 | return 0 28 | lm, hm = 1, 0 29 | low, high = a % n, n 30 | while low > 1: 31 | r = high//low 32 | nm, new = hm-lm*r, high-low*r 33 | lm, low, hm, high = nm, new, lm, low 34 | return lm % n 35 | 36 | def to_jacobian(p): 37 | o = (p[0], p[1], 1) 38 | return o 39 | 40 | def jacobian_double(p): 41 | if not p[1]: 42 | return (0, 0, 0) 43 | ysq = (p[1] ** 2) % P 44 | S = (4 * p[0] * ysq) % P 45 | M = (3 * p[0] ** 2 + A * p[2] ** 4) % P 46 | nx = (M**2 - 2 * S) % P 47 | ny = (M * (S - nx) - 8 * ysq ** 2) % P 48 | nz = (2 * p[1] * p[2]) % P 49 | return (nx, ny, nz) 50 | 51 | def jacobian_add(p, q): 52 | if not p[1]: 53 | return q 54 | if not q[1]: 55 | return p 56 | U1 = (p[0] * q[2] ** 2) % P 57 | U2 = (q[0] * p[2] ** 2) % P 58 | S1 = (p[1] * q[2] ** 3) % P 59 | S2 = (q[1] * p[2] ** 3) % P 60 | if U1 == U2: 61 | if S1 != S2: 62 | return (0, 0, 1) 63 | return jacobian_double(p) 64 | H = U2 - U1 65 | R = S2 - S1 66 | H2 = (H * H) % P 67 | H3 = (H * H2) % P 68 | U1H2 = (U1 * H2) % P 69 | nx = (R ** 2 - H3 - 2 * U1H2) % P 70 | ny = (R * (U1H2 - nx) - S1 * H3) % P 71 | nz = (H * p[2] * q[2]) % P 72 | return (nx, ny, nz) 73 | 74 | def from_jacobian(p): 75 | z = inv(p[2], P) 76 | return ((p[0] * z**2) % P, (p[1] * z**3) % P) 77 | 78 | def jacobian_multiply(a, n): 79 | if a[1] == 0 or n == 0: 80 | return (0, 0, 1) 81 | if n == 1: 82 | return a 83 | if n < 0 or n >= N: 84 | return jacobian_multiply(a, n % N) 85 | if (n % 2) == 0: 86 | return jacobian_double(jacobian_multiply(a, n//2)) 87 | if (n % 2) == 1: 88 | return jacobian_add(jacobian_double(jacobian_multiply(a, n//2)), a) 89 | 90 | def multiply(a, n): 91 | return from_jacobian(jacobian_multiply(to_jacobian(a), n)) 92 | 93 | def add(a, b): 94 | return from_jacobian(jacobian_add(to_jacobian(a), to_jacobian(b))) 95 | 96 | def privtopub(privkey): 97 | return multiply(G, bytes_to_int(privkey)) 98 | 99 | def deterministic_generate_k(msghash, priv): 100 | v = b'\x01' * 32 101 | k = b'\x00' * 32 102 | k = hmac.new(k, v+b'\x00'+priv+msghash, hashlib.sha256).digest() 103 | v = hmac.new(k, v, hashlib.sha256).digest() 104 | k = hmac.new(k, v+b'\x01'+priv+msghash, hashlib.sha256).digest() 105 | v = hmac.new(k, v, hashlib.sha256).digest() 106 | return bytes_to_int(hmac.new(k, v, hashlib.sha256).digest()) 107 | 108 | # bytes32, bytes32 -> v, r, s (as numbers) 109 | def ecdsa_raw_sign(msghash, priv): 110 | 111 | z = bytes_to_int(msghash) 112 | k = deterministic_generate_k(msghash, priv) 113 | 114 | r, y = multiply(G, k) 115 | s = inv(k, N) * (z + r*bytes_to_int(priv)) % N 116 | 117 | v, r, s = 27+((y % 2) ^ (0 if s * 2 < N else 1)), r, s if s * 2 < N else N - s 118 | return v, r, s 119 | 120 | def ecdsa_raw_recover(msghash, vrs): 121 | v, r, s = vrs 122 | if not (27 <= v <= 34): 123 | raise ValueError("%d must in range 27-31" % v) 124 | x = r 125 | xcubedaxb = (x*x*x+A*x+B) % P 126 | beta = pow(xcubedaxb, (P+1)//4, P) 127 | y = beta if v % 2 ^ beta % 2 else (P - beta) 128 | # If xcubedaxb is not a quadratic residue, then r cannot be the x coord 129 | # for a point on the curve, and so the sig is invalid 130 | if (xcubedaxb - y*y) % P != 0 or not (r % N) or not (s % N): 131 | return False 132 | z = bytes_to_int(msghash) 133 | Gz = jacobian_multiply((Gx, Gy, 1), (N - z) % N) 134 | XY = jacobian_multiply((x, y, 1), s) 135 | Qr = jacobian_add(Gz, XY) 136 | Q = jacobian_multiply(Qr, inv(r, N)) 137 | Q = from_jacobian(Q) 138 | 139 | return Q 140 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup, find_packages 4 | 5 | 6 | with open('README.md') as f: 7 | readme = f.read() 8 | 9 | with open('LICENSE') as f: 10 | license = f.read() 11 | 12 | setup( 13 | name='py_ecc', 14 | version='1.0.0', 15 | description='Elliptic curve crypto in python including secp256k1 and alt_bn128', 16 | long_description=readme, 17 | author='Vitalik Buterin', 18 | author_email='', 19 | url='https://github.com/ethereum/py_pairing', 20 | license=license, 21 | packages=find_packages(exclude=('tests', 'docs')), 22 | data_files=[ 23 | ('', ['LICENSE', 'README.md']) 24 | ], 25 | install_requires=[ 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /tests/test_bn128.py: -------------------------------------------------------------------------------- 1 | import time 2 | from py_ecc import bn128, optimized_bn128 3 | 4 | print('Starting bn128 tests') 5 | 6 | for lib in (bn128, optimized_bn128): 7 | FQ, FQ2, FQ12, field_modulus = lib.FQ, lib.FQ2, lib.FQ12, lib.field_modulus 8 | assert FQ(2) * FQ(2) == FQ(4) 9 | assert FQ(2) / FQ(7) + FQ(9) / FQ(7) == FQ(11) / FQ(7) 10 | assert FQ(2) * FQ(7) + FQ(9) * FQ(7) == FQ(11) * FQ(7) 11 | assert FQ(9) ** field_modulus == FQ(9) 12 | print('FQ works fine') 13 | 14 | x = FQ2([1, 0]) 15 | f = FQ2([1, 2]) 16 | fpx = FQ2([2, 2]) 17 | one = FQ2.one() 18 | assert x + f == fpx 19 | assert f / f == one 20 | assert one / f + x / f == (one + x) / f 21 | assert one * f + x * f == (one + x) * f 22 | assert x ** (field_modulus ** 2 - 1) == one 23 | print('FQ2 works fine') 24 | 25 | x = FQ12([1] + [0] * 11) 26 | f = FQ12([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) 27 | fpx = FQ12([2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) 28 | one = FQ12.one() 29 | assert x + f == fpx 30 | assert f / f == one 31 | assert one / f + x / f == (one + x) / f 32 | assert one * f + x * f == (one + x) * f 33 | # This check takes too long 34 | # assert x ** (field_modulus ** 12 - 1) == one 35 | print('FQ12 works fine') 36 | 37 | G1, G2, G12, b, b2, b12, is_inf, is_on_curve, eq, add, double, curve_order, multiply = \ 38 | lib.G1, lib.G2, lib.G12, lib.b, lib.b2, lib.b12, lib.is_inf, lib.is_on_curve, lib.eq, lib.add, lib.double, lib.curve_order, lib.multiply 39 | 40 | assert eq(add(add(double(G1), G1), G1), double(double(G1))) 41 | assert not eq(double(G1), G1) 42 | assert eq(add(multiply(G1, 9), multiply(G1, 5)), add(multiply(G1, 12), multiply(G1, 2))) 43 | assert is_inf(multiply(G1, curve_order)) 44 | print('G1 works fine') 45 | 46 | assert eq(add(add(double(G2), G2), G2), double(double(G2))) 47 | assert not eq(double(G2), G2) 48 | assert eq(add(multiply(G2, 9), multiply(G2, 5)), add(multiply(G2, 12), multiply(G2, 2))) 49 | assert is_inf(multiply(G2, curve_order)) 50 | assert not is_inf(multiply(G2, 2 * field_modulus - curve_order)) 51 | assert is_on_curve(multiply(G2, 9), b2) 52 | print('G2 works fine') 53 | 54 | assert eq(add(add(double(G12), G12), G12), double(double(G12))) 55 | assert not eq(double(G12), G12) 56 | assert eq(add(multiply(G12, 9), multiply(G12, 5)), add(multiply(G12, 12), multiply(G12, 2))) 57 | assert is_on_curve(multiply(G12, 9), b12) 58 | assert is_inf(multiply(G12, curve_order)) 59 | print('G12 works fine') 60 | 61 | pairing, neg = lib.pairing, lib.neg 62 | 63 | print('Starting pairing tests') 64 | a = time.time() 65 | p1 = pairing(G2, G1) 66 | pn1 = pairing(G2, neg(G1)) 67 | assert p1 * pn1 == FQ12.one() 68 | print('Pairing check against negative in G1 passed') 69 | np1 = pairing(neg(G2), G1) 70 | assert p1 * np1 == FQ12.one() 71 | assert pn1 == np1 72 | print('Pairing check against negative in G2 passed') 73 | assert p1 ** curve_order == FQ12.one() 74 | print('Pairing output has correct order') 75 | p2 = pairing(G2, multiply(G1, 2)) 76 | assert p1 * p1 == p2 77 | print('Pairing bilinearity in G1 passed') 78 | assert p1 != p2 and p1 != np1 and p2 != np1 79 | print('Pairing is non-degenerate') 80 | po2 = pairing(multiply(G2, 2), G1) 81 | assert p1 * p1 == po2 82 | print('Pairing bilinearity in G2 passed') 83 | p3 = pairing(multiply(G2, 27), multiply(G1, 37)) 84 | po3 = pairing(G2, multiply(G1, 999)) 85 | assert p3 == po3 86 | print('Composite check passed') 87 | print('Total time for pairings: %.3f' % (time.time() - a)) 88 | -------------------------------------------------------------------------------- /tests/test_secp256k1.py: -------------------------------------------------------------------------------- 1 | from py_ecc.secp256k1 import privtopub, ecdsa_raw_sign, ecdsa_raw_recover 2 | import binascii 3 | 4 | priv = binascii.unhexlify('792eca682b890b31356247f2b04662bff448b6bb19ea1c8ab48da222c894ef9b') 5 | pub = (20033694065814990006010338153307081985267967222430278129327181081381512401190L, 72089573118161052907088366229362685603474623289048716349537937839432544970413L) 6 | 7 | assert privtopub(priv) == pub 8 | v, r, s = ecdsa_raw_sign(b'\x35' * 32, priv) 9 | assert ecdsa_raw_recover(b'\x35' * 32, (v, r, s)) == pub 10 | print('secp256k1 tests passed') 11 | --------------------------------------------------------------------------------