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