bls12-381 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/bls/field.rb ADDED
@@ -0,0 +1,692 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BLS
4
+
5
+ # Finite field
6
+ module Field
7
+
8
+ def zero?
9
+ value.zero?
10
+ end
11
+
12
+ def negate
13
+ self.class.new(-value)
14
+ end
15
+
16
+ def ==(other)
17
+ value == other.value
18
+ end
19
+
20
+ def invert
21
+ x0 = 1
22
+ x1 = 0
23
+ y0 = 0
24
+ y1 = 1
25
+ a = self.class.const_get(:ORDER)
26
+ b = value
27
+ until a.zero?
28
+ q, b, a = [b / a, a, b % a]
29
+ x0, x1 = [x1, x0 - q * x1]
30
+ y0, y1 = [y1, y0 - q * y1]
31
+ end
32
+ self.class.new(x0)
33
+ end
34
+
35
+ def add(other)
36
+ self.class.new(value + other.value)
37
+ end
38
+ alias + add
39
+
40
+ def square
41
+ self.class.new(value**2)
42
+ end
43
+
44
+ def pow(n)
45
+ v = value.pow(n, self.class.const_get(:ORDER))
46
+ self.class.new(v)
47
+ end
48
+ alias ** pow
49
+
50
+ def subtract(other)
51
+ self.class.new(value - other.value)
52
+ end
53
+ alias - subtract
54
+
55
+ def multiply(other)
56
+ v = other.is_a?(Field) ? other.value : other
57
+ self.class.new(value * v)
58
+ end
59
+ alias * multiply
60
+
61
+ def div(other)
62
+ v = other.is_a?(Field) ? other.invert : self.class.new(other).invert
63
+ multiply(v)
64
+ end
65
+ alias / div
66
+ end
67
+
68
+ # Finite field over q.
69
+ class Fq
70
+ include Field
71
+
72
+ ORDER = BLS::Curve::P
73
+ MAX_BITS = Curve::P.bit_length
74
+
75
+ attr_reader :value
76
+
77
+ def initialize(value)
78
+ raise ArgumentError, 'Invalid value.' unless value.is_a?(Integer)
79
+
80
+ @value = BLS.mod(value, ORDER)
81
+ end
82
+
83
+ ZERO = Fq.new(0)
84
+ ONE = Fq.new(1)
85
+
86
+ end
87
+
88
+ # Finite field over r.
89
+ class Fr
90
+ include Field
91
+
92
+ ORDER = BLS::Curve::R
93
+
94
+ attr_reader :value
95
+
96
+ def initialize(value)
97
+ raise ArgumentError, 'Invalid value.' unless value.is_a?(Integer)
98
+
99
+ @value = BLS.mod(value, ORDER)
100
+ end
101
+
102
+ ZERO = Fr.new(0)
103
+ ONE = Fr.new(1)
104
+
105
+ def legendre
106
+ pow((order - 1) / 2)
107
+ end
108
+
109
+ end
110
+
111
+ # Module for a field over polynomial.
112
+ # TT - ThisType, CT - ChildType, TTT - Tuple Type
113
+ module FQP
114
+
115
+ def ==(other)
116
+ coeffs == other.coeffs
117
+ end
118
+
119
+ def zero?
120
+ coeffs.find { |c| !c.zero? }.nil?
121
+ end
122
+
123
+ def add(other)
124
+ self.class.new(coeffs.map.with_index { |v, i| v + other.coeffs[i] })
125
+ end
126
+ alias + add
127
+
128
+ def subtract(other)
129
+ self.class.new(coeffs.map.with_index { |v, i| v - other.coeffs[i] })
130
+ end
131
+ alias - subtract
132
+
133
+ def div(other)
134
+ inv = other.is_a?(Integer) ? Fq.new(other).invert.value : other.invert
135
+ multiply(inv)
136
+ end
137
+ alias / div
138
+
139
+ def negate
140
+ self.class.new(coeffs.map(&:negate))
141
+ end
142
+
143
+ def pow(n)
144
+ one = self.class.const_get(:ONE)
145
+ return one if n.zero?
146
+ return self if n == 1
147
+
148
+ p = one
149
+ d = self
150
+ while n.positive?
151
+ p *= d unless (n & 1).zero?
152
+ n >>= 1
153
+ d = d.square
154
+ end
155
+ p
156
+ end
157
+ alias ** pow
158
+
159
+ def conjugate
160
+ self.class.new([coeffs[0], coeffs[1].negate])
161
+ end
162
+ end
163
+
164
+ # Finite extension field over irreducible polynomial.
165
+ # Fq(u) / (u^2 - β) where β = -1
166
+ class Fq2
167
+ include FQP
168
+
169
+ # For Fq2 roots of unity.
170
+ RV1 = 0x6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09
171
+ EV1 = 0x699be3b8c6870965e5bf892ad5d2cc7b0e85a117402dfd83b7f4a947e02d978498255a2aaec0ac627b5afbdf1bf1c90
172
+ EV2 = 0x8157cd83046453f5dd0972b6e3949e4288020b5b8a9cc99ca07e27089a2ce2436d965026adad3ef7baba37f2183e9b5
173
+ EV3 = 0xab1c2ffdd6c253ca155231eb3e71ba044fd562f6f72bc5bad5ec46a0b7a3b0247cf08ce6c6317f40edbc653a72dee17
174
+ EV4 = 0xaa404866706722864480885d68ad0ccac1967c7544b447873cc37e0181271e006df72162a3d3e0287bf597fbf7f8fc1
175
+
176
+ ORDER = BLS::Curve::P2
177
+ MAX_BITS = Curve::P2.bit_length
178
+ COFACTOR = BLS::Curve::H2
179
+
180
+ attr_reader :coeffs
181
+
182
+ def initialize(coeffs)
183
+ raise ArgumentError, 'Expected array with 2 elements' unless coeffs.size == 2
184
+
185
+ @coeffs = coeffs.map { |c| c.is_a?(Integer) ? Fq.new(c) : c }
186
+ end
187
+
188
+ ROOT = Fq.new(-1)
189
+ ZERO = Fq2.new([0, 0])
190
+ ONE = Fq2.new([1, 0])
191
+
192
+ # Eighth roots of unity, used for computing square roots in Fq2.
193
+ ROOTS_OF_UNITY = [
194
+ Fq2.new([1, 0]),
195
+ Fq2.new([RV1, -RV1]),
196
+ Fq2.new([0, 1]),
197
+ Fq2.new([RV1, RV1]),
198
+ Fq2.new([-1, 0]),
199
+ Fq2.new([-RV1, RV1]),
200
+ Fq2.new([0, -1]),
201
+ Fq2.new([-RV1, -RV1])
202
+ ].freeze
203
+
204
+ # eta values, used for computing sqrt(g(X1(t)))
205
+ ETAS = [
206
+ Fq2.new([EV1, EV2]),
207
+ Fq2.new([-EV2, EV1]),
208
+ Fq2.new([EV3, EV4]),
209
+ Fq2.new([-EV4, EV3])
210
+ ].freeze
211
+
212
+ FROBENIUS_COEFFICIENTS = [
213
+ Fq.new(0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001),
214
+ Fq.new(0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa)
215
+ ].freeze
216
+
217
+ def values
218
+ coeffs.map(&:value)
219
+ end
220
+
221
+ def square
222
+ c0 = coeffs[0]
223
+ c1 = coeffs[1]
224
+ a = c0 + c1
225
+ b = c0 - c1
226
+ c = c0 + c0
227
+ Fq2.new([a * b, c * c1])
228
+ end
229
+
230
+ def multiply(other)
231
+ return Fq2.new(coeffs.map { |c| c * other }) if other.is_a?(Integer)
232
+
233
+ c0, c1 = coeffs
234
+ r0, r1 = other.coeffs
235
+ t1 = c0 * r0
236
+ t2 = c1 * r1
237
+ Fq2.new([t1 - t2, ((c0 + c1) * (r0 + r1)) - (t1 + t2)])
238
+ end
239
+ alias * multiply
240
+
241
+ def invert
242
+ a, b = values
243
+ factor = Fq.new(a * a + b * b).invert
244
+ Fq2.new([factor * a, factor * -b])
245
+ end
246
+
247
+ # Raises to q**i -th power
248
+ def frobenius_map(power)
249
+ Fq2.new([coeffs[0], coeffs[1] * Fq2::FROBENIUS_COEFFICIENTS[power % 2]])
250
+ end
251
+
252
+ def mul_by_non_residue
253
+ c0, c1 = coeffs
254
+ Fq2.new([c0 - c1, c0 + c1])
255
+ end
256
+
257
+ def multiply_by_b
258
+ c0, c1 = coeffs
259
+ t0 = c0 * 4
260
+ t1 = c1 * 4
261
+ Fq2.new([t0 - t1, t0 + t1])
262
+ end
263
+ end
264
+
265
+ # Finite extension field over irreducible polynomial.
266
+ # Fq2(v) / (v^3 - ξ) where ξ = u + 1
267
+ class Fq6
268
+ include FQP
269
+
270
+ attr_reader :coeffs
271
+
272
+ def initialize(coeffs)
273
+ raise ArgumentError, 'Expected array with 3 elements' unless coeffs.size == 3
274
+
275
+ @coeffs = coeffs
276
+ end
277
+
278
+ def self.from_tuple(t)
279
+ Fq6.new([Fq2.new(t[0...2]), Fq2.new(t[2...4]), Fq2.new(t[4...6])])
280
+ end
281
+
282
+ ZERO = Fq6.new([Fq2::ZERO, Fq2::ZERO, Fq2::ZERO])
283
+ ONE = Fq6.new([Fq2::ONE, Fq2::ZERO, Fq2::ZERO])
284
+
285
+ FROBENIUS_COEFFICIENTS_1 = [
286
+ Fq2.new([
287
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001,
288
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
289
+ ]),
290
+ Fq2.new([
291
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
292
+ 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac
293
+ ]),
294
+ Fq2.new([
295
+ 0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe,
296
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
297
+ ]),
298
+ Fq2.new([
299
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
300
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
301
+ ]),
302
+ Fq2.new([
303
+ 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac,
304
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
305
+ ]),
306
+ Fq2.new([
307
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
308
+ 0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe
309
+ ])
310
+ ].freeze
311
+
312
+ FROBENIUS_COEFFICIENTS_2 = [
313
+ Fq2.new([
314
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001,
315
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
316
+ ]),
317
+ Fq2.new([
318
+ 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad,
319
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
320
+ ]),
321
+ Fq2.new([
322
+ 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac,
323
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
324
+ ]),
325
+ Fq2.new([
326
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa,
327
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
328
+ ]),
329
+ Fq2.new([
330
+ 0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe,
331
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
332
+ ]),
333
+ Fq2.new([
334
+ 0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff,
335
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
336
+ ])
337
+ ].freeze
338
+
339
+ # Multiply by quadratic non-residue v.
340
+ def mul_by_non_residue
341
+ Fq6.new([coeffs[2].mul_by_non_residue, coeffs[0], coeffs[1]])
342
+ end
343
+
344
+ def multiply(other)
345
+ return Fq6.new([coeffs[0] * other, coeffs[1] * other, coeffs[2] * other]) if other.is_a?(Integer)
346
+
347
+ c0, c1, c2 = coeffs
348
+ r0, r1, r2 = other.coeffs
349
+ t0 = c0 * r0
350
+ t1 = c1 * r1
351
+ t2 = c2 * r2
352
+
353
+ Fq6.new([
354
+ t0 + ((c1 + c2) * (r1 + r2) - (t1 + t2)).mul_by_non_residue,
355
+ (c0 + c1) * (r0 + r1) - (t0 + t1) + t2.mul_by_non_residue,
356
+ t1 + ((c0 + c2) * (r0 + r2) - (t0 + t2))
357
+ ])
358
+ end
359
+ alias * multiply
360
+
361
+ # Sparse multiplication.
362
+ def multiply_by_1(b1)
363
+ Fq6.new([coeffs[2].multiply(b1).mul_by_non_residue, coeffs[0] * b1, coeffs[1] * b1])
364
+ end
365
+
366
+ # Sparse multiplication.
367
+ def multiply_by_01(b0, b1)
368
+ c0, c1, c2 = coeffs
369
+ t0 = c0 * b0
370
+ t1 = c1 * b1
371
+ Fq6.new([((c1 + c2) * b1 - t1).mul_by_non_residue + t0, (b0 + b1) * (c0 + c1) - t0 - t1, (c0 + c2) * b0 - t0 + t1])
372
+ end
373
+
374
+ def multiply_by_fq2(other)
375
+ Fq6.new(coeffs.map { |c| c * other })
376
+ end
377
+
378
+ def square
379
+ c0, c1, c2 = coeffs
380
+ t0 = c0.square
381
+ t1 = c0 * c1 * 2
382
+ t3 = c1 * c2 * 2
383
+ t4 = c2.square
384
+ Fq6.new([t3.mul_by_non_residue + t0, t4.mul_by_non_residue + t1, t1 + (c0 - c1 + c2).square + t3 - t0 - t4])
385
+ end
386
+
387
+ def invert
388
+ c0, c1, c2 = coeffs
389
+ t0 = c0.square - (c2 * c1).mul_by_non_residue
390
+ t1 = c2.square.mul_by_non_residue - (c0 * c1)
391
+ t2 = c1.square - c0 * c2
392
+ t4 = ((c2 * t1 + c1 * t2).mul_by_non_residue + c0 * t0).invert
393
+ Fq6.new([t4 * t0, t4 * t1, t4 * t2])
394
+ end
395
+
396
+ def frobenius_map(power)
397
+ Fq6.new([
398
+ coeffs[0].frobenius_map(power),
399
+ coeffs[1].frobenius_map(power) * Fq6::FROBENIUS_COEFFICIENTS_1[power % 6],
400
+ coeffs[2].frobenius_map(power) * Fq6::FROBENIUS_COEFFICIENTS_2[power % 6]
401
+ ])
402
+ end
403
+ end
404
+
405
+ # Finite extension field over irreducible polynomial.
406
+ # Fq6(w) / (w2 - γ) where γ = v
407
+ class Fq12
408
+ include FQP
409
+
410
+ attr_reader :coeffs
411
+
412
+ def initialize(coeffs)
413
+ raise ArgumentError, 'Expected array with 2 elements' unless coeffs.size == 2
414
+
415
+ @coeffs = coeffs
416
+ end
417
+
418
+ def self.from_tuple(t)
419
+ Fq12.new([Fq6.from_tuple(t[0...6]), Fq6.from_tuple(t[6...12])])
420
+ end
421
+
422
+ ZERO = Fq12.new([Fq6::ZERO, Fq6::ZERO])
423
+ ONE = Fq12.new([Fq6::ONE, Fq6::ZERO])
424
+
425
+ FROBENIUS_COEFFICIENTS = [
426
+ Fq2.new([
427
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001,
428
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
429
+ ]),
430
+ Fq2.new([
431
+ 0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8,
432
+ 0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3
433
+ ]),
434
+ Fq2.new([
435
+ 0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffeffff,
436
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
437
+ ]),
438
+ Fq2.new([
439
+ 0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2,
440
+ 0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09
441
+ ]),
442
+ Fq2.new([
443
+ 0x00000000000000005f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe,
444
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
445
+ ]),
446
+ Fq2.new([
447
+ 0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995,
448
+ 0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116
449
+ ]),
450
+ Fq2.new([
451
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa,
452
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
453
+ ]),
454
+ Fq2.new([
455
+ 0x00fc3e2b36c4e03288e9e902231f9fb854a14787b6c7b36fec0c8ec971f63c5f282d5ac14d6c7ec22cf78a126ddc4af3,
456
+ 0x1904d3bf02bb0667c231beb4202c0d1f0fd603fd3cbd5f4f7b2443d784bab9c4f67ea53d63e7813d8d0775ed92235fb8
457
+ ]),
458
+ Fq2.new([
459
+ 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac,
460
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
461
+ ]),
462
+ Fq2.new([
463
+ 0x06af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09,
464
+ 0x135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2
465
+ ]),
466
+ Fq2.new([
467
+ 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad,
468
+ 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
469
+ ]),
470
+ Fq2.new([
471
+ 0x05b2cfd9013a5fd8df47fa6b48b1e045f39816240c0b8fee8beadf4d8e9c0566c63a3e6e257f87329b18fae980078116,
472
+ 0x144e4211384586c16bd3ad4afa99cc9170df3560e77982d0db45f3536814f0bd5871c1908bd478cd1ee605167ff82995
473
+ ])
474
+ ].freeze
475
+
476
+ def multiply(other)
477
+ return Fq12.new([coeffs[0] * other, coeffs[1] * other]) if other.is_a?(Integer)
478
+
479
+ c0, c1 = coeffs
480
+ r0, r1 = other.coeffs
481
+ t1 = c0 * r0
482
+ t2 = c1 * r1
483
+ Fq12.new([t1 + t2.mul_by_non_residue, (c0 + c1) * (r0 + r1) - (t1 + t2)])
484
+ end
485
+ alias * multiply
486
+
487
+ def multiply_by_014(o0, o1, o4)
488
+ c0, c1 = coeffs
489
+ t0 = c0.multiply_by_01(o0, o1)
490
+ t1 = c1.multiply_by_1(o4)
491
+ Fq12.new([t1.mul_by_non_residue + t0, (c1 + c0).multiply_by_01(o0, o1 + o4) - t0 - t1])
492
+ end
493
+
494
+ def multiply_by_fq2(other)
495
+ Fq12.new(coeffs.map{ |c| c.multiply_by_fq2(other) })
496
+ end
497
+
498
+ def square
499
+ c0, c1 = coeffs
500
+ ab = c0 * c1
501
+ Fq12.new([(c1.mul_by_non_residue + c0) * (c0 + c1) - ab - ab.mul_by_non_residue, ab + ab])
502
+ end
503
+
504
+ def invert
505
+ c0, c1 = coeffs
506
+ t = (c0.square - c1.square.mul_by_non_residue).invert
507
+ Fq12.new([c0 * t, (c1 * t).negate])
508
+ end
509
+
510
+ def frobenius_map(power)
511
+ c0, c1 = coeffs
512
+ r0 = c0.frobenius_map(power)
513
+ c1_0, c1_1, c1_2 = c1.frobenius_map(power).coeffs
514
+ Fq12.new([
515
+ r0,
516
+ Fq6.new([
517
+ c1_0 * Fq12::FROBENIUS_COEFFICIENTS[power % 12],
518
+ c1_1 * Fq12::FROBENIUS_COEFFICIENTS[power % 12],
519
+ c1_2 * Fq12::FROBENIUS_COEFFICIENTS[power % 12]])])
520
+ end
521
+
522
+ def final_exponentiate
523
+ t0 = frobenius_map(6) / self
524
+ t1 = t0.frobenius_map(2) * t0
525
+ t2 = t1.cyclotomic_exp(Curve::X).conjugate
526
+ t3 = t1.cyclotomic_square.conjugate * t2
527
+ t4 = t3.cyclotomic_exp(Curve::X).conjugate
528
+ t5 = t4.cyclotomic_exp(Curve::X).conjugate
529
+ t6 = t5.cyclotomic_exp(Curve::X).conjugate * t2.cyclotomic_square
530
+ (t2 * t5).frobenius_map(2) * (t4 * t1).frobenius_map(3) *
531
+ (t6 * t1.conjugate).frobenius_map(1) * t6.cyclotomic_exp(Curve::X).conjugate * t3.conjugate * t1
532
+ end
533
+
534
+ def cyclotomic_square
535
+ c0, c1 = coeffs
536
+ c0c0, c0c1, c0c2 = c0.coeffs
537
+ c1c0, c1c1, c1c2 = c1.coeffs
538
+ t3, t4 = fq4_square(c0c0, c1c1)
539
+ t5, t6 = fq4_square(c1c0, c0c2)
540
+ t7, t8 = fq4_square(c0c1, c1c2)
541
+ t9 = t8.mul_by_non_residue
542
+ Fq12.new([
543
+ Fq6.new([(t3 - c0c0) * 2 + t3, (t5 - c0c1) * 2 + t5, (t7 - c0c2) * 2 + t7]),
544
+ Fq6.new([(t9 + c1c0) * 2 + t9, (t4 + c1c1) * 2 + t4, (t6 + c1c2) * 2 + t6])])
545
+ end
546
+
547
+ def cyclotomic_exp(n)
548
+ z = Fq12::ONE
549
+ i = BLS_X_LEN - 1
550
+ while i >= 0
551
+ z = z.cyclotomic_square
552
+ z *= self unless BLS.bit_get(n, i).zero?
553
+ i -= 1
554
+ end
555
+ z
556
+ end
557
+
558
+ private
559
+
560
+ # @param [Fq2] a
561
+ # @param [Fq2] b
562
+ # @return [Array]
563
+ def fq4_square(a, b)
564
+ a2 = a.square
565
+ b2 = b.square
566
+ [b2.mul_by_non_residue + a2, (a + b).square - a2 - b2]
567
+ end
568
+
569
+ end
570
+
571
+ UT_ROOT = BLS::Fq6.new([BLS::Fq2::ZERO, BLS::Fq2::ONE, BLS::Fq2::ZERO])
572
+ WSQ = BLS::Fq12.new([UT_ROOT, BLS::Fq6::ZERO])
573
+ WSQ_INV = WSQ.invert
574
+ WCU = BLS::Fq12.new([BLS::Fq6::ZERO, UT_ROOT])
575
+ WCU_INV = WCU.invert
576
+ # 1 / F2(2)^((p - 1) / 3) in GF(p^2)
577
+ PSI2_C1 = 0x1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac
578
+ BLS_X_LEN = Curve::X.bit_length
579
+
580
+ P_MINUS_9_DIV_16 = (Curve::P**2 - 9) / 16
581
+
582
+ XNUM = [
583
+ Fq2.new([
584
+ 0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6,
585
+ 0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6]),
586
+ Fq2.new([
587
+ 0x0,
588
+ 0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a]),
589
+ Fq2.new([
590
+ 0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e,
591
+ 0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d]),
592
+ Fq2.new([
593
+ 0x171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1,
594
+ 0x0])
595
+ ].freeze
596
+ XDEN = [
597
+ Fq2.new([
598
+ 0x0,
599
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa63]),
600
+ Fq2.new([
601
+ 0xc,
602
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f]),
603
+ Fq2::ONE,
604
+ Fq2::ZERO
605
+ ].freeze
606
+ YNUM = [
607
+ Fq2.new([
608
+ 0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706,
609
+ 0x1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706]),
610
+ Fq2.new([
611
+ 0x0,
612
+ 0x5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be]),
613
+ Fq2.new([
614
+ 0x11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c,
615
+ 0x8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f]),
616
+ Fq2.new([
617
+ 0x124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10,
618
+ 0x0])
619
+ ].freeze
620
+ YDEN = [
621
+ Fq2.new([
622
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb,
623
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb]),
624
+ Fq2.new([
625
+ 0x0,
626
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3]),
627
+ Fq2.new([
628
+ 0x12,
629
+ 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99]),
630
+ Fq2.new([0x1, 0x0])
631
+ ].freeze
632
+
633
+ ISOGENY_COEFFICIENTS = [XNUM, XDEN, YNUM, YDEN]
634
+
635
+ module_function
636
+
637
+ def psi(x, y)
638
+ x2 = WSQ_INV.multiply_by_fq2(x).frobenius_map(1).multiply(WSQ).coeffs[0].coeffs[0]
639
+ y2 = WCU_INV.multiply_by_fq2(y).frobenius_map(1).multiply(WCU).coeffs[0].coeffs[0]
640
+ [x2, y2]
641
+ end
642
+
643
+ def psi2(x, y)
644
+ [x * PSI2_C1, y.negate]
645
+ end
646
+
647
+ def miller_loop(ell, g1)
648
+ f12 = Fq12::ONE
649
+ p_x, p_y = g1
650
+ i = BLS_X_LEN - 2
651
+ j = 0
652
+ while i >= 0
653
+ f12 = f12.multiply_by_014(ell[j][0], ell[j][1] * p_x.value, ell[j][2] * p_y.value)
654
+ unless bit_get(Curve::X, i).zero?
655
+ j += 1
656
+ f12 = f12.multiply_by_014(ell[j][0], ell[j][1] * p_x.value, ell[j][2] * p_y.value)
657
+ end
658
+ f12 = f12.square unless i.zero?
659
+ i -= 1
660
+ j += 1
661
+ end
662
+ f12.conjugate
663
+ end
664
+
665
+
666
+
667
+ def sgn0(x)
668
+ x0, x1 = x.values
669
+ sign_0 = x0 % 2
670
+ zero_0 = x0 === 0
671
+ sign_1 = x1 % 2
672
+ sign_0 || (zero_0 && sign_1)
673
+ end
674
+
675
+ def sqrt_div_fq2(u, v)
676
+ uv7 = u * v**7
677
+ uv15 = uv7 * v**8
678
+ gamma = uv15**P_MINUS_9_DIV_16 * uv7
679
+ success = false
680
+ result = gamma
681
+ positive_roots_of_unity = Fq2::ROOTS_OF_UNITY[0...4]
682
+ positive_roots_of_unity.each do |root|
683
+ candidate = root * gamma
684
+ if (candidate**2 * v - u).zero? && !success
685
+ success = true
686
+ result = candidate
687
+ end
688
+ end
689
+ [success, result]
690
+ end
691
+
692
+ end