bls12-381 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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