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/math.rb ADDED
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BLS
4
+
5
+ module_function
6
+
7
+ def mod(a, b)
8
+ res = a % b
9
+ res >= 0 ? res : b + res
10
+ end
11
+
12
+ def pow_mod(a, power, m)
13
+ res = 1
14
+ while power.positive?
15
+ res = mod(res * a, m) unless (power & 1).zero?
16
+ power >>= 1
17
+ a = mod(a * a, m)
18
+ end
19
+ res
20
+ end
21
+
22
+ def bit_get(n, pos)
23
+ (n >> pos) & 1
24
+ end
25
+
26
+ # Convert byte to non-negative integer.
27
+ # @param [Array[Integer]] bytes byte array.
28
+ # @return [Array[Integer]] byte array.
29
+ def os2ip(bytes)
30
+ res = 0
31
+ bytes.each do |b|
32
+ res <<= 8
33
+ res += b
34
+ end
35
+ res
36
+ end
37
+
38
+ # Convert +value+ to byte array of +length+.
39
+ # @param [Integer] value
40
+ # @param [Integer] length
41
+ # @return [Array[Integer] byte array.
42
+ # @raise [BLS::Error]
43
+ def i2osp(value, length)
44
+ raise BLS::Error, "bad I2OSP call: value=#{value} length=#{length}" if value < 0 || value >= (1 << 8 * length)
45
+
46
+ res = Array.new(length, 0)
47
+ i = length - 1
48
+ while i >= 0
49
+ res[i] = value & 0xff
50
+ value >>= 8
51
+ i -= 1
52
+ end
53
+ res
54
+ end
55
+
56
+ # Calculate binary xor between +a+ and +b+.
57
+ # @param [String] a binary string.
58
+ # @param [String] b binary string.
59
+ # @return [String] xor binary string.
60
+ def bin_xor(a, b)
61
+ res = Array.new(a.bytesize)
62
+ b_bytes = b.bytes
63
+ a.bytes.each.with_index do |b, i|
64
+ res[i] = b ^ b_bytes[i]
65
+ end
66
+ res.pack('C*')
67
+ end
68
+
69
+ # Optimized SWU Map - FQ2 to G2': y^2 = x^3 + 240i * x + 1012 + 1012i
70
+ def map_to_curve_sswu_g2(t)
71
+ iso_3_a = Fq2.new([0, 240])
72
+ iso_3_b = Fq2.new([1012, 1012])
73
+ iso_3_z = Fq2.new([-2, -1])
74
+ t = Fq2.new(t) if t.is_a?(Array)
75
+ t2 = t**2
76
+ iso_3_z_t2 = iso_3_z * t2
77
+ ztzt = iso_3_z_t2 + iso_3_z_t2**2
78
+ denominator = (iso_3_a * ztzt).negate
79
+ numerator = iso_3_b * (ztzt + Fq2::ONE)
80
+ denominator = iso_3_z * iso_3_a if denominator.zero?
81
+ v = denominator**3
82
+ u = numerator**3 + iso_3_a * numerator * denominator**2 + iso_3_b * v
83
+ success, sqrt_candidate_or_gamma = BLS.sqrt_div_fq2(u, v)
84
+ y = success ? sqrt_candidate_or_gamma : nil
85
+ sqrt_candidate_x1 = sqrt_candidate_or_gamma * t**3
86
+ u = iso_3_z_t2**3 * u
87
+ success2 = false
88
+ Fq2::ETAS.each do |eta|
89
+ eta_sqrt_candidate = eta * sqrt_candidate_x1
90
+ temp = eta_sqrt_candidate**2 * v - u
91
+ if temp.zero? && !success && !success2
92
+ y = eta_sqrt_candidate
93
+ success2 = true
94
+ end
95
+ end
96
+ raise BLS::PointError, 'Hash to Curve - Optimized SWU failure' if !success && !success2
97
+
98
+ numerator *= iso_3_z_t2 if success2
99
+ y = y.negate if BLS.sgn0(t) != BLS.sgn0(y)
100
+ y *= denominator
101
+ [numerator, y, denominator]
102
+ end
103
+
104
+ # 3-isogeny map from E' to E
105
+ # Converts from Jacobi (xyz) to Projective (xyz) coordinates.
106
+ def isogeny_map_g2(x, y, z)
107
+ mapped = Array.new(4, Fq2::ZERO)
108
+ z_powers = [z, z**2, z**3]
109
+ ISOGENY_COEFFICIENTS.each.with_index do |k, i|
110
+ mapped[i] = k[-1]
111
+ arr = k[0...-1].reverse
112
+ arr.each.with_index do |a, j|
113
+ mapped[i] = mapped[i] * x + z_powers[j] * a
114
+ end
115
+ end
116
+ mapped[2] *= y
117
+ mapped[3] *= z
118
+ z2 = mapped[1] * mapped[3]
119
+ x2 = mapped[0] * mapped[3]
120
+ y2 = mapped[1] * mapped[2]
121
+ [x2, y2, z2]
122
+ end
123
+
124
+ # Normalize private key.
125
+ # @param [String|Integer] private_key a private key with hex or number.
126
+ # @return [BLS::Fq] private key field.
127
+ # @raise [BLS::Error] Occur when the private key is zero.
128
+ def normalize_priv_key(private_key)
129
+ k = private_key.is_a?(String) ? private_key.to_i(16) : private_key
130
+ fq = Fq.new(k)
131
+ raise BLS::Error, 'Private key cannot be 0' if fq.zero?
132
+
133
+ fq
134
+ end
135
+
136
+ # Convert number to +byte_length+ bytes hex string.
137
+ # @param [Integer] num number tobe converted.
138
+ # @param [Integer] byte_length byte length.
139
+ # @return [String] hex value.
140
+ def num_to_hex(num, byte_length)
141
+ num.to_s(16).rjust(2 * byte_length, '0')
142
+ end
143
+
144
+ end
@@ -0,0 +1,20 @@
1
+ module BLS
2
+ module_function
3
+
4
+ class PairingError < StandardError; end
5
+
6
+ # @param [BLS::PointG1] p
7
+ # @param [BLS::PointG2] q
8
+ # @param [Boolean] with_final_exp
9
+ # @return [BLS::Fq12]
10
+ # @return [BLS::PairingError] Occur when p.zero? or q.zero?
11
+ def pairing(p, q, with_final_exp: true)
12
+ raise PairingError, 'No pairings at point of Infinity' if p.zero? || q.zero?
13
+
14
+ p.validate!
15
+ q.validate!
16
+ looped = p.miller_loop(q)
17
+ with_final_exp ? looped.final_exponentiate : looped
18
+ end
19
+
20
+ end
data/lib/bls/point.rb ADDED
@@ -0,0 +1,509 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+
5
+ module BLS
6
+
7
+ class PointError < StandardError; end
8
+
9
+ # Abstract Point class that consist of projective coordinates.
10
+ class ProjectivePoint
11
+
12
+ attr_reader :x, :y, :z
13
+ attr_accessor :m_precomputes
14
+
15
+ def initialize(x, y, z)
16
+ @x = x
17
+ @y = y
18
+ @z = z
19
+ @m_precomputes = nil
20
+ end
21
+
22
+ def zero?
23
+ z.zero?
24
+ end
25
+
26
+ def zero
27
+ one = x.class.const_get(:ONE)
28
+ new_point(one, one, x.class.const_get(:ZERO))
29
+ end
30
+
31
+ def new_point(x, y, z)
32
+ self.class.new(x, y, z)
33
+ end
34
+
35
+ # Compare one point to another.
36
+ # @param [ProjectivePoint] other another point.
37
+ # @return [Boolean] whether same point or not.
38
+ def ==(other)
39
+ raise PointError, "ProjectivePoint#==: this is #{self.class}, but other is #{other.class}" unless self.class == other.class
40
+
41
+ (x * other.z) == (other.x * z) && (y * other.z) == (other.y * z)
42
+ end
43
+
44
+ def negate
45
+ new_point(x, y.negate, z)
46
+ end
47
+
48
+ # http://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#doubling-dbl-1998-cmo-2
49
+ def double
50
+ w = x * x * 3
51
+ s = y * z
52
+ ss = s * s
53
+ sss = ss * s
54
+ b = x * y * s
55
+ h = w * w - ( b * 8)
56
+ x3 = h * s * 2
57
+ y3 = w * (b * 4 - h) - (y * y * 8 * ss) # W * (4 * B - H) - 8 * y * y * S_squared
58
+ z3 = sss * 8
59
+ new_point(x3, y3, z3)
60
+ end
61
+
62
+ # http://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#addition-add-1998-cmo-2
63
+ def add(other)
64
+ raise PointError, "ProjectivePoint#add: this is #{self.class}, but other is #{other.class}" unless self.class == other.class
65
+
66
+ return other if zero?
67
+ return self if other.zero?
68
+
69
+ x1 = self.x
70
+ y1 = self.y
71
+ z1 = self.z
72
+ x2 = other.x
73
+ y2 = other.y
74
+ z2 = other.z
75
+ u1 = y2 * z1
76
+ u2 = y1 * z2
77
+ v1 = x2 * z1
78
+ v2 = x1 * z2
79
+ return double if v1 == v2 && u1 == u2
80
+ return zero if v1 == v2
81
+
82
+ u = u1 - u2
83
+ v = v1 - v2
84
+ vv = v * v
85
+ vvv = vv * v
86
+ v2vv = v2 * vv
87
+ w = z1 * z2
88
+ a = u * u * w - vvv - v2vv * 2
89
+ x3 = v * a
90
+ y3 = u * (v2vv - a) - vvv * u2
91
+ z3 = vvv * w
92
+ new_point(x3, y3, z3)
93
+ end
94
+ alias + add
95
+
96
+ def subtract(other)
97
+ raise PointError, "ProjectivePoint#subtract: this is #{self.class}, but other is #{other.class}" unless self.class == other.class
98
+
99
+ add(other.negate)
100
+ end
101
+ alias - subtract
102
+
103
+ def multiply_unsafe(scalar)
104
+ n = scalar.is_a?(Fq) ? scalar.value : scalar
105
+ raise PointError, 'Point#multiply: invalid scalar, expected positive integer' if n <= 0
106
+
107
+ p = zero
108
+ d = self
109
+ while n.positive?
110
+ p += d unless (n & 1).zero?
111
+ d = d.double
112
+ n >>= 1
113
+ end
114
+ p
115
+ end
116
+
117
+ def to_affine(inv_z = z.invert)
118
+ [x * inv_z, y * inv_z]
119
+ end
120
+
121
+ def to_affine_batch(points)
122
+ to_inv = gen_invert_batch(points.map(&:z))
123
+ points.map.with_index { |p, i| p.to_affine(to_inv[i]) }
124
+ end
125
+
126
+ def from_affine_tuple(xy)
127
+ new_point(xy[0], xy[1], x.class.const_get(:ONE))
128
+ end
129
+
130
+ def gen_invert_batch(nums)
131
+ len = nums.length
132
+ scratch = Array.new(len)
133
+ acc = x.class::ONE
134
+ len.times do |i|
135
+ next if nums[i].zero?
136
+
137
+ scratch[i] = acc
138
+ acc *= nums[i]
139
+ end
140
+ acc = acc.invert
141
+ len.times do |t|
142
+ i = len - t - 1
143
+ next if nums[i].zero?
144
+
145
+ tmp = acc * nums[i]
146
+ nums[i] = acc * scratch[i]
147
+ acc = tmp
148
+ end
149
+ nums
150
+ end
151
+
152
+ # Constant time multiplication. Uses wNAF.
153
+ def multiply(scalar)
154
+ n = scalar.is_a?(Fq) ? scalar.value : scalar
155
+ raise PointError, 'Invalid scalar, expected positive integer' if n <= 0
156
+ raise PointError, "Scalar has more bits than maxBits, shouldn't happen" if n.bit_length > max_bits
157
+
158
+ wNAF(n).first
159
+ end
160
+ alias * multiply
161
+
162
+ def precomputes_window(w)
163
+ windows = (BigDecimal(max_bits) / w).ceil
164
+ window_size = 2**(w - 1)
165
+ points = []
166
+ p = self
167
+ windows.times do
168
+ base = p
169
+ points << base
170
+ (1...window_size).each do
171
+ base += p
172
+ points << base
173
+ end
174
+ p = base.double
175
+ end
176
+ points
177
+ end
178
+
179
+ def max_bits
180
+ self.class.const_get(:MAX_BITS)
181
+ end
182
+
183
+ def normalize_z(points)
184
+ to_affine_batch(points).map{ |p| from_affine_tuple(p) }
185
+ end
186
+
187
+ def calc_multiply_precomputes(w)
188
+ raise PointError, 'This point already has precomputes.' if m_precomputes
189
+
190
+ self.m_precomputes = [w, normalize_z(precomputes_window(w))]
191
+ end
192
+
193
+ def clear_multiply_precomputes
194
+ self.m_precomputes = nil
195
+ end
196
+
197
+ private
198
+
199
+ def wNAF(n)
200
+ w, precomputes = m_precomputes || [1, precomputes_window(1)]
201
+ p = zero
202
+ f = zero
203
+ windows = (BigDecimal(max_bits) / w).ceil
204
+ window_size = 2**(w - 1)
205
+ mask = (2**w - 1)
206
+ max_number = 2**w
207
+ shift_by = w
208
+ windows.times do |window|
209
+ offset = window * window_size
210
+ wbits = n & mask
211
+ n >>= shift_by
212
+ if wbits > window_size
213
+ wbits -= max_number
214
+ n += 1
215
+ end
216
+ if wbits.zero?
217
+ f += (window % 2 ? precomputes[offset].negate : precomputes[offset])
218
+ else
219
+ cached = precomputes[offset + wbits.abs - 1]
220
+ p += (wbits.negative? ? cached.negate : cached)
221
+ end
222
+ end
223
+ [p, f]
224
+ end
225
+ end
226
+
227
+ class PointG1 < ProjectivePoint
228
+
229
+ BASE = PointG1.new(Fq.new(Curve::G_X), Fq.new(Curve::G_Y), Fq::ONE)
230
+ ZERO = PointG1.new(Fq::ONE, Fq::ONE, Fq::ZERO)
231
+ MAX_BITS = Fq::MAX_BITS
232
+
233
+ # Parse PointG1 from form hex.
234
+ # @param [String] hex hex value of PointG1.
235
+ # @return [PointG1]
236
+ # @raise [BLS::PointError] Occurs when hex length does not match, or point does not on G1.
237
+ def self.from_hex(hex)
238
+ bytes = [hex].pack('H*')
239
+ point = case bytes.bytesize
240
+ when 48
241
+ compressed_value = hex.to_i(16)
242
+ b_flag = BLS.mod(compressed_value, POW_2_383) / POW_2_382
243
+ return ZERO if b_flag == 1
244
+
245
+ x = BLS.mod(compressed_value, POW_2_381)
246
+ full_y = BLS.mod(x**3 + Fq.new(Curve::B).value, Curve::P)
247
+ y = BLS.pow_mod(full_y, (Curve::P + 1) / 4, Curve::P)
248
+ raise PointError, 'The given point is not on G1: y**2 = x**3 + b.' unless (BLS.pow_mod(y, 2, Curve::P) - full_y).zero?
249
+
250
+ a_flag = BLS.mod(compressed_value, POW_2_382) / POW_2_381
251
+ y = Curve::P - y unless ((y * 2) / Curve::P) == a_flag
252
+ PointG1.new(Fq.new(x), Fq.new(y), Fq::ONE)
253
+ when 96
254
+ return ZERO unless (bytes[0].unpack1('H*').to_i(16) & (1 << 6)).zero?
255
+
256
+ x = bytes[0...PUBLIC_KEY_LENGTH].unpack1('H*').to_i(16)
257
+ y = bytes[PUBLIC_KEY_LENGTH..-1].unpack1('H*').to_i(16)
258
+ PointG1.new(Fq.new(x), Fq.new(y), Fq::ONE)
259
+ else
260
+ raise PointError, 'Invalid point G1, expected 48 or 96 bytes.'
261
+ end
262
+ point.validate!
263
+ point
264
+ end
265
+
266
+ def to_hex(compressed: false)
267
+ if compressed
268
+ if self == PointG1::ZERO
269
+ hex = POW_2_383 + POW_2_382
270
+ else
271
+ x, y = to_affine
272
+ flag = (y.value * 2) / Curve::P
273
+ hex = x.value + flag * POW_2_381 + POW_2_383
274
+ end
275
+ BLS.num_to_hex(hex, PUBLIC_KEY_LENGTH)
276
+ else
277
+ if self == PointG1::ZERO
278
+ (1 << 6).to_s(16) + '00' * (2 * PUBLIC_KEY_LENGTH - 1)
279
+ else
280
+ x, y = to_affine
281
+ BLS.num_to_hex(x.value, PUBLIC_KEY_LENGTH) + BLS.num_to_hex(y.value, PUBLIC_KEY_LENGTH)
282
+ end
283
+ end
284
+ end
285
+
286
+ # Parse Point from private key.
287
+ # @param [String|Integer] private_key a private key with hex or number.
288
+ # @return [PointG1] G1Point corresponding to private keys.
289
+ # @raise [BLS::Error] Occur when the private key is zero.
290
+ def self.from_private_key(private_key)
291
+ BASE * BLS.normalize_priv_key(private_key)
292
+ end
293
+
294
+ # Validate this point whether on curve over Fq.
295
+ # @raise [PointError] Occur when this point not on curve over Fq.
296
+ def validate!
297
+ b = Fq.new(Curve::B)
298
+ return if zero?
299
+
300
+ left = y.pow(2) * z - x.pow(3)
301
+ right = b * z.pow(3)
302
+ raise PointError, 'Invalid point: not on curve over Fq' unless left == right
303
+ end
304
+
305
+ # Sparse multiplication against precomputed coefficients.
306
+ # @param [PointG2] p
307
+ def miller_loop(p)
308
+ BLS.miller_loop(p.pairing_precomputes, to_affine)
309
+ end
310
+ end
311
+
312
+ class PointG2 < ProjectivePoint
313
+
314
+ attr_accessor :precomputes
315
+
316
+ MAX_BITS = Fq2::MAX_BITS
317
+ BASE = PointG2.new(Fq2.new(Curve::G2_X), Fq2.new(Curve::G2_Y), Fq2::ONE)
318
+ ZERO = PointG2.new(Fq2::ONE, Fq2::ONE, Fq2::ZERO)
319
+
320
+ # Parse PointG1 from form hex.
321
+ # @param [String] hex hex value of PointG2. Currently, only uncompressed formats(196 bytes) are supported.
322
+ # @return [BLS::PointG2] PointG2 object.
323
+ # @raise [BLS::PointError]
324
+ def self.from_hex(hex)
325
+ bytes = [hex].pack('H*')
326
+ point = case bytes.bytesize
327
+ when 96
328
+ raise PointError, 'Compressed format not supported yet.'
329
+ when 192
330
+ return ZERO unless (bytes[0].unpack1('H*').to_i(16) & (1 << 6)).zero?
331
+
332
+ x1 = bytes[0...PUBLIC_KEY_LENGTH].unpack1('H*').to_i(16)
333
+ x0 = bytes[PUBLIC_KEY_LENGTH...(2 * PUBLIC_KEY_LENGTH)].unpack1('H*').to_i(16)
334
+ y1 = bytes[(2 * PUBLIC_KEY_LENGTH)...(3 * PUBLIC_KEY_LENGTH)].unpack1('H*').to_i(16)
335
+ y0 = bytes[(3 * PUBLIC_KEY_LENGTH)..-1].unpack1('H*').to_i(16)
336
+ PointG2.new(Fq2.new([x0, x1]), Fq2.new([y0, y1]), Fq2::ONE)
337
+ else
338
+ raise PointError, 'Invalid uncompressed point G2, expected 192 bytes.'
339
+ end
340
+ point.validate!
341
+ point
342
+ end
343
+
344
+ # Convert hash to PointG2
345
+ # @param [String] message a hash with hex format.
346
+ # @return [BLS::PointG2] point.
347
+ # @raise [BLS::PointError]
348
+ def self.hash_to_curve(message)
349
+ raise PointError, 'expected hex string' unless message[/^[a-fA-F0-9]*$/]
350
+
351
+ u = BLS.hash_to_field(message, 2)
352
+ q0 = PointG2.new(*BLS.isogeny_map_g2(*BLS.map_to_curve_sswu_g2(u[0])))
353
+ q1 = PointG2.new(*BLS.isogeny_map_g2(*BLS.map_to_curve_sswu_g2(u[1])))
354
+ r = q0 + q1
355
+ BLS.clear_cofactor_g2(r)
356
+ end
357
+
358
+ def to_hex(compressed: false)
359
+ raise ArgumentError, 'Not supported' if compressed
360
+
361
+ if self == PointG2::ZERO
362
+ (1 << 6).to_s(16) + '00' * (4 * PUBLIC_KEY_LENGTH - 1)
363
+ else
364
+ validate!
365
+ x, y = to_affine.map(&:values)
366
+ BLS.num_to_hex(x[1], PUBLIC_KEY_LENGTH) +
367
+ BLS.num_to_hex(x[0], PUBLIC_KEY_LENGTH) +
368
+ BLS.num_to_hex(y[1], PUBLIC_KEY_LENGTH) +
369
+ BLS.num_to_hex(y[0], PUBLIC_KEY_LENGTH)
370
+ end
371
+ end
372
+
373
+ # Convert to signature with hex format.
374
+ # @return [String] signature with hex format.
375
+ def to_signature
376
+ if self == PointG2::ZERO
377
+ sum = POW_2_383 + POW_2_382
378
+ return BLS.num_to_hex(sum, PUBLIC_KEY_LENGTH) + BLS.num_to_hex(0, PUBLIC_KEY_LENGTH)
379
+ end
380
+ validate!
381
+ x, y = to_affine.map(&:values)
382
+ tmp = y[1] > 0 ? y[1] * 2 : y[0] * 2
383
+ aflag1 = tmp / Curve::P
384
+ z1 = x[1] + aflag1 * POW_2_381 + POW_2_383
385
+ z2 = x[0]
386
+ BLS.num_to_hex(z1, PUBLIC_KEY_LENGTH) + BLS.num_to_hex(z2, PUBLIC_KEY_LENGTH)
387
+ end
388
+
389
+ def validate!
390
+ b = Fq2.new(Curve::B2)
391
+ return if zero?
392
+
393
+ left = y.pow(2) * z - x.pow(3)
394
+ right = b * z.pow(3)
395
+ raise PointError, 'Invalid point: not on curve over Fq2' unless left == right
396
+ end
397
+
398
+ def clear_pairing_precomputes
399
+ self.precomputes = nil
400
+ end
401
+
402
+ def pairing_precomputes
403
+ return precomputes if precomputes
404
+
405
+ self.precomputes = calc_pairing_precomputes(*to_affine)
406
+ precomputes
407
+ end
408
+
409
+ private
410
+
411
+ def calc_pairing_precomputes(x, y)
412
+ q_x, q_y, q_z = [x, y, Fq2::ONE]
413
+ r_x, r_y, r_z = [q_x, q_y, q_z]
414
+ ell_coeff = []
415
+ i = BLS_X_LEN - 2
416
+ while i >= 0
417
+ t0 = r_y.square
418
+ t1 = r_z.square
419
+ t2 = t1.multiply(3).multiply_by_b
420
+ t3 = t2 * 3
421
+ t4 = (r_y + r_z).square - t1 - t0
422
+ ell_coeff << [t2 - t0, r_x.square * 3, t4.negate]
423
+ r_x = (t0 - t3) * r_x * r_y / 2
424
+ r_y = ((t0 + t3) / 2).square - t2.square * 3
425
+ r_z = t0 * t4
426
+ unless BLS.bit_get(Curve::X, i).zero?
427
+ t0 = r_y - q_y * r_z
428
+ t1 = r_x - q_x * r_z
429
+ ell_coeff << [t0 * q_x - t1 * q_y, t0.negate, t1]
430
+ t2 = t1.square
431
+ t3 = t2 * t1
432
+ t4 = t2 * r_x
433
+ t5 = t3 - t4 * 2 + t0.square * r_z
434
+ r_x = t1 * t5
435
+ r_y = (t4 - t5) * t0 - t3 * r_y
436
+ r_z *= t3
437
+ end
438
+ i -= 1
439
+ end
440
+ ell_coeff
441
+ end
442
+ end
443
+
444
+ module_function
445
+
446
+ def clear_cofactor_g2(p)
447
+ t1 = p.multiply_unsafe(Curve::X).negate
448
+ t2 = p.from_affine_tuple(BLS.psi(*p.to_affine))
449
+ p2 = p.from_affine_tuple(BLS.psi2(*p.double.to_affine))
450
+ p2 - t2 + (t1 + t2).multiply_unsafe(Curve::X).negate - t1 - p
451
+ end
452
+
453
+ def norm_p1(point)
454
+ point.is_a?(PointG1) ? point : PointG1.from_hex(point)
455
+ end
456
+
457
+ def norm_p2(point)
458
+ point.is_a?(PointG2) ? point : PointG2.from_hex(point)
459
+ end
460
+
461
+ def norm_p2h(point)
462
+ point.is_a?(PointG2) ? point : PointG2.hash_to_curve(point)
463
+ end
464
+
465
+ # Convert hash to Field.
466
+ # @param [String] message hash value with hex format.
467
+ # @return [Array[Integer]] byte array.
468
+ def hash_to_field(message, degree, random_oracle: true)
469
+ count = random_oracle ? 2 : 1
470
+ l = 64
471
+ len_in_bytes = count * degree * l
472
+ pseudo_random_bytes = BLS.expand_message_xmd(message, len_in_bytes)
473
+ u = Array.new(count)
474
+ count.times do |i|
475
+ e = Array.new(degree)
476
+ degree.times do |j|
477
+ elm_offset = l * (j + i * degree)
478
+ tv = pseudo_random_bytes[elm_offset...(elm_offset + l)]
479
+ e[j] = BLS.mod(BLS.os2ip(tv), Curve::P)
480
+ end
481
+ u[i] = e
482
+ end
483
+ u
484
+ end
485
+
486
+ # @param [String] message hash value with hex format.
487
+ # @param [Integer] len_in_bytes length
488
+ # @return [Array[Integer]] byte array.
489
+ # @raise BLS::Error
490
+ def expand_message_xmd(message, len_in_bytes)
491
+ b_in_bytes = BigDecimal(SHA256_DIGEST_SIZE)
492
+ r_in_bytes = b_in_bytes * 2
493
+ ell = (BigDecimal(len_in_bytes) / b_in_bytes).ceil
494
+ raise BLS::Error, 'Invalid xmd length' if ell > 255
495
+
496
+ dst_prime = DST_LABEL.bytes + BLS.i2osp(DST_LABEL.bytesize, 1)
497
+ z_pad = BLS.i2osp(0, r_in_bytes)
498
+ l_i_b_str = BLS.i2osp(len_in_bytes, 2)
499
+ b = Array.new(ell)
500
+ payload = z_pad + [message].pack('H*').bytes + l_i_b_str + BLS.i2osp(0, 1) + dst_prime
501
+ b_0 = Digest::SHA256.digest(payload.pack('C*'))
502
+ b[0] = Digest::SHA256.digest((b_0.bytes + BLS.i2osp(1, 1) + dst_prime).pack('C*'))
503
+ (1..ell).each do |i|
504
+ args = BLS.bin_xor(b_0, b[i - 1]).bytes + BLS.i2osp(i + 1, 1) + dst_prime
505
+ b[i] = Digest::SHA256.digest(args.pack('C*'))
506
+ end
507
+ b.map(&:bytes).flatten[0...len_in_bytes]
508
+ end
509
+ end