5
5
Union ,
6
6
)
7
7
8
+ from eth_utils import (
9
+ big_endian_to_int ,
10
+ )
8
11
9
- from py_ecc .optimized_bn128 import ( # NOQA
12
+ from py_ecc .optimized_bls12_381 import ( # NOQA
10
13
G1 ,
11
14
G2 ,
12
15
Z1 ,
20
23
FQP ,
21
24
pairing ,
22
25
normalize ,
23
- field_modulus ,
26
+ field_modulus as q ,
24
27
b ,
25
28
b2 ,
26
29
is_on_curve ,
27
30
curve_order ,
28
31
final_exponentiate
29
32
)
30
- from eth .utils .blake import blake
31
- from eth .utils .bn128 import (
32
- FQP_point_to_FQ2_point ,
33
- )
33
+ from eth_hash .auto import keccak as hash
34
34
35
35
36
- CACHE = {} # type: Dict[bytes, Tuple[FQ2, FQ2, FQ2]]
37
- # 16th root of unity
38
- HEX_ROOT = FQ2 ([21573744529824266246521972077326577680729363968861965890554801909984373949499 ,
39
- 16854739155576650954933913186877292401521110422362946064090026408937773542853 ])
36
+ G2_cofactor = 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109 # noqa: E501
37
+ qmod = q ** 2 - 1
38
+ eighth_roots_of_unity = [
39
+ FQ2 ([1 , 1 ]) ** ((qmod * k ) // 8 )
40
+ for k in range (8 )
41
+ ]
40
42
41
43
42
- assert HEX_ROOT ** 8 != FQ2 ([1 , 0 ])
43
- assert HEX_ROOT ** 16 == FQ2 ([1 , 0 ])
44
+ #
45
+ # Helpers
46
+ #
47
+ def FQP_point_to_FQ2_point (pt : Tuple [FQP , FQP , FQP ]) -> Tuple [FQ2 , FQ2 , FQ2 ]:
48
+ """
49
+ Transform FQP to FQ2 for type hinting.
50
+ """
51
+ return (
52
+ FQ2 (pt [0 ].coeffs ),
53
+ FQ2 (pt [1 ].coeffs ),
54
+ FQ2 (pt [2 ].coeffs ),
55
+ )
44
56
45
57
58
+ def modular_squareroot (value : int ) -> int :
59
+ """
60
+ ``modular_squareroot(x)`` returns the value ``y`` such that ``y**2 % q == x``,
61
+ and None if this is not possible. In cases where there are two solutions,
62
+ the value with higher imaginary component is favored;
63
+ if both solutions have equal imaginary component the value with higher real
64
+ component is favored.
65
+ """
66
+ candidate_squareroot = value ** ((qmod + 8 ) // 16 )
67
+ check = candidate_squareroot ** 2 / value
68
+ if check in eighth_roots_of_unity [::2 ]:
69
+ x1 = candidate_squareroot / eighth_roots_of_unity [eighth_roots_of_unity .index (check ) // 2 ]
70
+ x2 = FQ2 ([- x1 .coeffs [0 ], - x1 .coeffs [1 ]])
71
+ # x2 = - x2
72
+ return x1 if (x1 .coeffs [1 ], x1 .coeffs [0 ]) > (x2 .coeffs [1 ], x2 .coeffs [0 ]) else x2
73
+ return None
74
+
75
+
76
+ def hash_to_G2 (message : bytes , domain : int ) -> Tuple [FQ2 , FQ2 , FQ2 ]:
77
+ domain_in_bytes = domain .to_bytes (8 , 'big' )
78
+ x1 = big_endian_to_int (hash (domain_in_bytes + b'\x01 ' + message ))
79
+ x2 = big_endian_to_int (hash (domain_in_bytes + b'\x02 ' + message ))
80
+ x_coordinate = FQ2 ([x1 , x2 ]) # x1 + x2 * i
81
+ while 1 :
82
+ x_cubed_plus_b2 = x_coordinate ** 3 + FQ2 ([4 , 4 ])
83
+ y_coordinate = modular_squareroot (x_cubed_plus_b2 )
84
+ if y_coordinate is not None :
85
+ break
86
+ x_coordinate += FQ2 ([1 , 0 ]) # Add one until we get a quadratic residue
87
+
88
+ return multiply (
89
+ (x_coordinate , y_coordinate , FQ2 ([1 , 0 ])),
90
+ G2_cofactor
91
+ )
92
+
93
+
94
+ #
95
+ # G1
96
+ #
46
97
def compress_G1 (pt : Tuple [FQ , FQ , FQ ]) -> int :
47
98
x , y = normalize (pt )
48
- return x .n + 2 ** 255 * (y .n % 2 )
99
+ return x .n + 2 ** 383 * (y .n % 2 )
49
100
50
101
51
102
def decompress_G1 (p : int ) -> Tuple [FQ , FQ , FQ ]:
52
103
if p == 0 :
53
104
return (FQ (1 ), FQ (1 ), FQ (0 ))
54
- x = p % 2 ** 255
55
- y_mod_2 = p // 2 ** 255
56
- y = pow ((x ** 3 + b .n ) % field_modulus , (field_modulus + 1 ) // 4 , field_modulus )
57
- assert pow (y , 2 , field_modulus ) == (x ** 3 + b .n ) % field_modulus
105
+ x = p % 2 ** 383
106
+ y_mod_2 = p // 2 ** 383
107
+ y = pow ((x ** 3 + b .n ) % q , (q + 1 ) // 4 , q )
108
+ assert pow (y , 2 , q ) == (x ** 3 + b .n ) % q
58
109
if y % 2 != y_mod_2 :
59
- y = field_modulus - y
110
+ y = q - y
60
111
return (FQ (x ), FQ (y ), FQ (1 ))
61
112
62
113
63
- def sqrt_fq2 (x : FQP ) -> FQ2 :
64
- y = x ** ((field_modulus ** 2 + 15 ) // 32 )
65
- while y ** 2 != x :
66
- y *= HEX_ROOT
67
- return FQ2 (y .coeffs )
68
-
69
-
70
- def hash_to_G2 (m : bytes ) -> Tuple [FQ2 , FQ2 , FQ2 ]:
71
- """
72
- WARNING: this function has not been standardized yet.
73
- """
74
- if m in CACHE :
75
- return CACHE [m ]
76
- k2 = m
77
- while 1 :
78
- k1 = blake (k2 )
79
- k2 = blake (k1 )
80
- x1 = int .from_bytes (k1 , 'big' ) % field_modulus
81
- x2 = int .from_bytes (k2 , 'big' ) % field_modulus
82
- x = FQ2 ([x1 , x2 ])
83
- xcb = x ** 3 + b2
84
- if xcb ** ((field_modulus ** 2 - 1 ) // 2 ) == FQ2 ([1 , 0 ]):
85
- break
86
- y = sqrt_fq2 (xcb )
87
-
88
- o = FQP_point_to_FQ2_point (multiply ((x , y , FQ2 ([1 , 0 ])), 2 * field_modulus - curve_order ))
89
- CACHE [m ] = o
90
- return o
91
-
92
-
114
+ #
115
+ # G2
116
+ #
93
117
def compress_G2 (pt : Tuple [FQP , FQP , FQP ]) -> Tuple [int , int ]:
94
118
assert is_on_curve (pt , b2 )
95
119
x , y = normalize (pt )
96
120
return (
97
- int (x .coeffs [0 ] + 2 ** 255 * (y .coeffs [0 ] % 2 )),
121
+ int (x .coeffs [0 ] + 2 ** 383 * (y .coeffs [0 ] % 2 )),
98
122
int (x .coeffs [1 ])
99
123
)
100
124
101
125
102
126
def decompress_G2 (p : bytes ) -> Tuple [FQP , FQP , FQP ]:
103
- x1 = p [0 ] % 2 ** 255
104
- y1_mod_2 = p [0 ] // 2 ** 255
127
+ x1 = p [0 ] % 2 ** 383
128
+ y1_mod_2 = p [0 ] // 2 ** 383
105
129
x2 = p [1 ]
106
130
x = FQ2 ([x1 , x2 ])
107
131
if x == FQ2 ([0 , 0 ]):
108
132
return FQ2 ([1 , 0 ]), FQ2 ([1 , 0 ]), FQ2 ([0 , 0 ])
109
- y = sqrt_fq2 (x ** 3 + b2 )
133
+ y = modular_squareroot (x ** 3 + b2 )
110
134
if y .coeffs [0 ] % 2 != y1_mod_2 :
111
135
y = FQ2 ((y * - 1 ).coeffs )
112
136
assert is_on_curve ((x , y , FQ2 ([1 , 0 ])), b2 )
113
137
return x , y , FQ2 ([1 , 0 ])
114
138
115
139
116
- def sign (m : bytes , k : int ) -> Tuple [int , int ]:
117
- return compress_G2 (multiply (hash_to_G2 (m ), k ))
140
+ #
141
+ # APIs
142
+ #
143
+ def sign (message : bytes ,
144
+ privkey : int ,
145
+ domain : int ) -> Tuple [int , int ]:
146
+ return compress_G2 (
147
+ multiply (
148
+ hash_to_G2 (message , domain ),
149
+ privkey
150
+ )
151
+ )
118
152
119
153
120
154
def privtopub (k : int ) -> int :
121
155
return compress_G1 (multiply (G1 , k ))
122
156
123
157
124
- def verify (m : bytes , pub : int , sig : bytes ) -> bool :
158
+ def verify (m : bytes , pub : int , sig : bytes , domain : int ) -> bool :
125
159
final_exponentiation = final_exponentiate (
126
160
pairing (FQP_point_to_FQ2_point (decompress_G2 (sig )), G1 , False ) *
127
- pairing (FQP_point_to_FQ2_point (hash_to_G2 (m )), neg (decompress_G1 (pub )), False )
161
+ pairing (FQP_point_to_FQ2_point (hash_to_G2 (m , domain )), neg (decompress_G1 (pub )), False )
128
162
)
129
163
return final_exponentiation == FQ12 .one ()
130
164
@@ -141,3 +175,22 @@ def aggregate_pubs(pubs: Iterable[int]) -> int:
141
175
for p in pubs :
142
176
o = add (o , decompress_G1 (p ))
143
177
return compress_G1 (o )
178
+
179
+
180
+ def multi_verify (pubs , msgs , sig , domain ):
181
+ len_msgs = len (msgs )
182
+ assert len (pubs ) == len_msgs
183
+
184
+ o = FQ12 ([1 ] + [0 ] * 11 )
185
+ for m in set (msgs ):
186
+ # aggregate the pubs
187
+ group_pub = Z1
188
+ for i in range (len_msgs ):
189
+ if msgs [i ] == m :
190
+ group_pub = add (group_pub , decompress_G1 (pubs [i ]))
191
+
192
+ o *= pairing (hash_to_G2 (m , domain ), group_pub , False )
193
+ o *= pairing (decompress_G2 (sig ), neg (G1 ), False )
194
+
195
+ final_exponentiation = final_exponentiate (o )
196
+ return final_exponentiation == FQ12 .one ()
0 commit comments