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/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