bls12-381 0.2.2 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +5 -3
- data/bls12-381.gemspec +1 -1
- data/lib/bls/field.rb +163 -136
- data/lib/bls/h2c.rb +114 -0
- data/lib/bls/math.rb +4 -61
- data/lib/bls/pairing.rb +1 -1
- data/lib/bls/point/g1.rb +105 -0
- data/lib/bls/point/g2.rb +188 -0
- data/lib/bls/point.rb +13 -276
- data/lib/bls/version.rb +1 -1
- data/lib/bls.rb +89 -36
- metadata +19 -2
data/lib/bls/point.rb
CHANGED
@@ -1,9 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bigdecimal'
|
4
|
+
require 'h2c'
|
4
5
|
|
5
6
|
module BLS
|
6
7
|
|
8
|
+
# Point serialization flags
|
9
|
+
POINT_COMPRESSION_FLAG = 0x80
|
10
|
+
POINT_INFINITY_FLAG = 0x40
|
11
|
+
POINT_Y_FLAG = 0x20
|
12
|
+
|
13
|
+
autoload :PointG1, "bls/point/g1"
|
14
|
+
autoload :PointG2, "bls/point/g2"
|
15
|
+
|
7
16
|
class PointError < StandardError; end
|
8
17
|
|
9
18
|
# Abstract Point class that consist of projective coordinates.
|
@@ -224,240 +233,8 @@ module BLS
|
|
224
233
|
end
|
225
234
|
end
|
226
235
|
|
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
|
-
# Parse Point from private key.
|
345
|
-
# @param [String|Integer] private_key a private key with hex or number.
|
346
|
-
# @return [PointG1] G1Point corresponding to private keys.
|
347
|
-
# @raise [BLS::Error] Occur when the private key is zero.
|
348
|
-
def self.from_private_key(private_key)
|
349
|
-
BASE * BLS.normalize_priv_key(private_key)
|
350
|
-
end
|
351
|
-
|
352
|
-
# Convert hash to PointG2
|
353
|
-
# @param [String] message a hash with hex format.
|
354
|
-
# @return [BLS::PointG2] point.
|
355
|
-
# @raise [BLS::PointError]
|
356
|
-
def self.hash_to_curve(message)
|
357
|
-
raise PointError, 'expected hex string' unless message[/^[a-fA-F0-9]*$/]
|
358
|
-
|
359
|
-
u = BLS.hash_to_field(message, 2)
|
360
|
-
q0 = PointG2.new(*BLS.isogeny_map_g2(*BLS.map_to_curve_sswu_g2(u[0])))
|
361
|
-
q1 = PointG2.new(*BLS.isogeny_map_g2(*BLS.map_to_curve_sswu_g2(u[1])))
|
362
|
-
r = q0 + q1
|
363
|
-
BLS.clear_cofactor_g2(r)
|
364
|
-
end
|
365
|
-
|
366
|
-
def to_hex(compressed: false)
|
367
|
-
raise ArgumentError, 'Not supported' if compressed
|
368
|
-
|
369
|
-
if self == PointG2::ZERO
|
370
|
-
(1 << 6).to_s(16) + '00' * (4 * PUBLIC_KEY_LENGTH - 1)
|
371
|
-
else
|
372
|
-
validate!
|
373
|
-
x, y = to_affine.map(&:values)
|
374
|
-
BLS.num_to_hex(x[1], PUBLIC_KEY_LENGTH) +
|
375
|
-
BLS.num_to_hex(x[0], PUBLIC_KEY_LENGTH) +
|
376
|
-
BLS.num_to_hex(y[1], PUBLIC_KEY_LENGTH) +
|
377
|
-
BLS.num_to_hex(y[0], PUBLIC_KEY_LENGTH)
|
378
|
-
end
|
379
|
-
end
|
380
|
-
|
381
|
-
# Convert to signature with hex format.
|
382
|
-
# @return [String] signature with hex format.
|
383
|
-
def to_signature
|
384
|
-
if self == PointG2::ZERO
|
385
|
-
sum = POW_2_383 + POW_2_382
|
386
|
-
return BLS.num_to_hex(sum, PUBLIC_KEY_LENGTH) + BLS.num_to_hex(0, PUBLIC_KEY_LENGTH)
|
387
|
-
end
|
388
|
-
validate!
|
389
|
-
x, y = to_affine.map(&:values)
|
390
|
-
tmp = y[1] > 0 ? y[1] * 2 : y[0] * 2
|
391
|
-
aflag1 = tmp / Curve::P
|
392
|
-
z1 = x[1] + aflag1 * POW_2_381 + POW_2_383
|
393
|
-
z2 = x[0]
|
394
|
-
BLS.num_to_hex(z1, PUBLIC_KEY_LENGTH) + BLS.num_to_hex(z2, PUBLIC_KEY_LENGTH)
|
395
|
-
end
|
396
|
-
|
397
|
-
def validate!
|
398
|
-
b = Fq2.new(Curve::B2)
|
399
|
-
return if zero?
|
400
|
-
|
401
|
-
left = y.pow(2) * z - x.pow(3)
|
402
|
-
right = b * z.pow(3)
|
403
|
-
raise PointError, 'Invalid point: not on curve over Fq2' unless left == right
|
404
|
-
end
|
405
|
-
|
406
|
-
def clear_pairing_precomputes
|
407
|
-
self.precomputes = nil
|
408
|
-
end
|
409
|
-
|
410
|
-
def pairing_precomputes
|
411
|
-
return precomputes if precomputes
|
412
|
-
|
413
|
-
self.precomputes = calc_pairing_precomputes(*to_affine)
|
414
|
-
precomputes
|
415
|
-
end
|
416
|
-
|
417
|
-
private
|
418
|
-
|
419
|
-
def calc_pairing_precomputes(x, y)
|
420
|
-
q_x, q_y, q_z = [x, y, Fq2::ONE]
|
421
|
-
r_x, r_y, r_z = [q_x, q_y, q_z]
|
422
|
-
ell_coeff = []
|
423
|
-
i = BLS_X_LEN - 2
|
424
|
-
while i >= 0
|
425
|
-
t0 = r_y.square
|
426
|
-
t1 = r_z.square
|
427
|
-
t2 = t1.multiply(3).multiply_by_b
|
428
|
-
t3 = t2 * 3
|
429
|
-
t4 = (r_y + r_z).square - t1 - t0
|
430
|
-
ell_coeff << [t2 - t0, r_x.square * 3, t4.negate]
|
431
|
-
r_x = (t0 - t3) * r_x * r_y / 2
|
432
|
-
r_y = ((t0 + t3) / 2).square - t2.square * 3
|
433
|
-
r_z = t0 * t4
|
434
|
-
unless BLS.bit_get(Curve::X, i).zero?
|
435
|
-
t0 = r_y - q_y * r_z
|
436
|
-
t1 = r_x - q_x * r_z
|
437
|
-
ell_coeff << [t0 * q_x - t1 * q_y, t0.negate, t1]
|
438
|
-
t2 = t1.square
|
439
|
-
t3 = t2 * t1
|
440
|
-
t4 = t2 * r_x
|
441
|
-
t5 = t3 - t4 * 2 + t0.square * r_z
|
442
|
-
r_x = t1 * t5
|
443
|
-
r_y = (t4 - t5) * t0 - t3 * r_y
|
444
|
-
r_z *= t3
|
445
|
-
end
|
446
|
-
i -= 1
|
447
|
-
end
|
448
|
-
ell_coeff
|
449
|
-
end
|
450
|
-
end
|
451
|
-
|
452
236
|
module_function
|
453
237
|
|
454
|
-
def clear_cofactor_g2(p)
|
455
|
-
t1 = p.multiply_unsafe(Curve::X).negate
|
456
|
-
t2 = p.from_affine_tuple(BLS.psi(*p.to_affine))
|
457
|
-
p2 = p.from_affine_tuple(BLS.psi2(*p.double.to_affine))
|
458
|
-
p2 - t2 + (t1 + t2).multiply_unsafe(Curve::X).negate - t1 - p
|
459
|
-
end
|
460
|
-
|
461
238
|
def norm_p1(point)
|
462
239
|
point.is_a?(PointG1) ? point : PointG1.from_hex(point)
|
463
240
|
end
|
@@ -466,52 +243,12 @@ module BLS
|
|
466
243
|
point.is_a?(PointG2) ? point : PointG2.from_hex(point)
|
467
244
|
end
|
468
245
|
|
469
|
-
def
|
470
|
-
point.is_a?(
|
246
|
+
def norm_p1h(point)
|
247
|
+
point.is_a?(PointG1) ? point : PointG1.hash_to_curve(point)
|
471
248
|
end
|
472
249
|
|
473
|
-
|
474
|
-
|
475
|
-
# @return [Array[Integer]] byte array.
|
476
|
-
def hash_to_field(message, degree, random_oracle: true)
|
477
|
-
count = random_oracle ? 2 : 1
|
478
|
-
l = 64
|
479
|
-
len_in_bytes = count * degree * l
|
480
|
-
pseudo_random_bytes = BLS.expand_message_xmd(message, len_in_bytes)
|
481
|
-
u = Array.new(count)
|
482
|
-
count.times do |i|
|
483
|
-
e = Array.new(degree)
|
484
|
-
degree.times do |j|
|
485
|
-
elm_offset = l * (j + i * degree)
|
486
|
-
tv = pseudo_random_bytes[elm_offset...(elm_offset + l)]
|
487
|
-
e[j] = BLS.mod(BLS.os2ip(tv), Curve::P)
|
488
|
-
end
|
489
|
-
u[i] = e
|
490
|
-
end
|
491
|
-
u
|
250
|
+
def norm_p2h(point)
|
251
|
+
point.is_a?(PointG2) ? point : PointG2.hash_to_curve(point)
|
492
252
|
end
|
493
253
|
|
494
|
-
# @param [String] message hash value with hex format.
|
495
|
-
# @param [Integer] len_in_bytes length
|
496
|
-
# @return [Array[Integer]] byte array.
|
497
|
-
# @raise BLS::Error
|
498
|
-
def expand_message_xmd(message, len_in_bytes)
|
499
|
-
b_in_bytes = BigDecimal(SHA256_DIGEST_SIZE)
|
500
|
-
r_in_bytes = b_in_bytes * 2
|
501
|
-
ell = (BigDecimal(len_in_bytes) / b_in_bytes).ceil
|
502
|
-
raise BLS::Error, 'Invalid xmd length' if ell > 255
|
503
|
-
|
504
|
-
dst_prime = DST_LABEL.bytes + BLS.i2osp(DST_LABEL.bytesize, 1)
|
505
|
-
z_pad = BLS.i2osp(0, r_in_bytes)
|
506
|
-
l_i_b_str = BLS.i2osp(len_in_bytes, 2)
|
507
|
-
b = Array.new(ell)
|
508
|
-
payload = z_pad + [message].pack('H*').bytes + l_i_b_str + BLS.i2osp(0, 1) + dst_prime
|
509
|
-
b_0 = Digest::SHA256.digest(payload.pack('C*'))
|
510
|
-
b[0] = Digest::SHA256.digest((b_0.bytes + BLS.i2osp(1, 1) + dst_prime).pack('C*'))
|
511
|
-
(1..ell).each do |i|
|
512
|
-
args = BLS.bin_xor(b_0, b[i - 1]).bytes + BLS.i2osp(i + 1, 1) + dst_prime
|
513
|
-
b[i] = Digest::SHA256.digest(args.pack('C*'))
|
514
|
-
end
|
515
|
-
b.map(&:bytes).flatten[0...len_in_bytes]
|
516
|
-
end
|
517
254
|
end
|
data/lib/bls/version.rb
CHANGED
data/lib/bls.rb
CHANGED
@@ -5,6 +5,7 @@ require 'bls/version'
|
|
5
5
|
require 'bls/math'
|
6
6
|
require 'bls/curve'
|
7
7
|
require 'bls/field'
|
8
|
+
require 'bls/h2c'
|
8
9
|
require 'bls/point'
|
9
10
|
require 'bls/pairing'
|
10
11
|
|
@@ -19,82 +20,134 @@ module BLS
|
|
19
20
|
PUBLIC_KEY_LENGTH = 48
|
20
21
|
SHA256_DIGEST_SIZE = 32
|
21
22
|
|
22
|
-
DST_LABEL = 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_'
|
23
|
-
|
24
23
|
module_function
|
25
24
|
|
26
25
|
# Generate BLS signature: s = pk x H(m)
|
27
|
-
# @param [String] message Message digest(
|
26
|
+
# @param [String] message Message digest(hex format) to be signed.
|
28
27
|
# @param [Integer|String] private_key The private key used for signing. Integer or String(hex).
|
28
|
+
# @param [Symbol] sig_type Signature type, :g1 or :g2.
|
29
|
+
# If :g1 is specified, the signature is a point on G1 and the public key is a point on G2.
|
30
|
+
# If :g2 is specified, the signature is a point on G2 and the public key is a point on G1.
|
29
31
|
# @return [PointG2] The signature point.
|
30
|
-
def sign(message, private_key)
|
31
|
-
msg_point =
|
32
|
+
def sign(message, private_key, sig_type: :g2)
|
33
|
+
msg_point = case sig_type
|
34
|
+
when :g1
|
35
|
+
BLS.norm_p1h(message)
|
36
|
+
when :g2
|
37
|
+
BLS.norm_p2h(message)
|
38
|
+
else
|
39
|
+
raise Error, 'sig_type must be :g1 or :g2.'
|
40
|
+
end
|
32
41
|
msg_point * BLS.normalize_priv_key(private_key)
|
33
42
|
end
|
34
43
|
|
35
44
|
# Generate public key from +private_key+.
|
36
45
|
# @param [Integer|String] private_key The private key. Integer or String(hex).
|
37
|
-
# @
|
38
|
-
|
39
|
-
|
46
|
+
# @param [Symbol] key_type Public key type, :g1 or :g2.
|
47
|
+
# @return [BLS::PointG1|BLS::PointG2] public key.
|
48
|
+
def get_public_key(private_key, key_type: :g1)
|
49
|
+
case key_type
|
50
|
+
when :g1
|
51
|
+
PointG1.from_private_key(private_key)
|
52
|
+
when :g2
|
53
|
+
PointG2.from_private_key(private_key)
|
54
|
+
else
|
55
|
+
raise Error, 'key_type must be :g1 or :g2.'
|
56
|
+
end
|
40
57
|
end
|
41
58
|
|
42
|
-
# Verify BLS signature.
|
43
|
-
#
|
59
|
+
# Verify BLS signature. Verify one of the following:
|
60
|
+
# * Public key is a point on G1, signature is a point on G2 or
|
61
|
+
# * Public key is a point on G2, signature is a point on G1.
|
62
|
+
# @param [BLS::PointG1|BLS::PointG2] signature
|
44
63
|
# @param [String] message Message digest(hash value with hex format) to be verified.
|
45
|
-
# @param [
|
64
|
+
# @param [BLS::PointG2|BLS::PointG1] public_key Public key with hex format or PointG1.
|
46
65
|
# @return [Boolean] verification result.
|
47
66
|
def verify(signature, message, public_key)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
67
|
+
unless signature.is_a?(PointG1) && public_key.is_a?(PointG2) ||
|
68
|
+
signature.is_a?(PointG2) && public_key.is_a?(PointG1)
|
69
|
+
raise BLS::Error, 'Invalid signature or public key. If the public key is PointG1, the signature must be an element of Point::G2 or vice versa.'
|
70
|
+
end
|
71
|
+
g = public_key.is_a?(PointG1) ? PointG1::BASE : PointG2::BASE
|
72
|
+
ephm = if public_key.is_a?(PointG1)
|
73
|
+
hm = BLS.norm_p2h(message)
|
74
|
+
BLS.pairing(public_key.negate, hm, with_final_exp: false)
|
75
|
+
else
|
76
|
+
hm = BLS.norm_p1h(message)
|
77
|
+
BLS.pairing(hm, public_key.negate, with_final_exp: false)
|
78
|
+
end
|
79
|
+
egs = if public_key.is_a?(PointG1)
|
80
|
+
BLS.pairing(g, signature, with_final_exp: false)
|
81
|
+
else
|
82
|
+
BLS.pairing(signature, g, with_final_exp: false)
|
83
|
+
end
|
54
84
|
exp = (egs * ephm).final_exponentiate
|
55
|
-
exp ==
|
85
|
+
exp == Fp12::ONE
|
56
86
|
end
|
57
87
|
|
58
88
|
# Aggregate multiple public keys.
|
59
|
-
# @param [Array[
|
60
|
-
# @return [BLS::PointG1] aggregated public key.
|
89
|
+
# @param [Array[BLS::PointG1]|Array[BLS::PointG2]] public_keys the list of public keys.
|
90
|
+
# @return [BLS::PointG1|BLS::PointG2] aggregated public key.
|
61
91
|
def aggregate_public_keys(public_keys)
|
62
92
|
raise BLS::Error, 'Expected non-empty array.' if public_keys.empty?
|
63
|
-
|
64
|
-
|
93
|
+
g1_flag = public_keys.first.is_a?(PointG1)
|
94
|
+
sum = g1_flag ? PointG1::ZERO : PointG2::ZERO
|
95
|
+
public_keys.each do |pubkey|
|
96
|
+
if g1_flag && !pubkey.is_a?(PointG1) || !g1_flag && !pubkey.is_a?(PointG2)
|
97
|
+
raise BLS::Error, 'Point G1 and G2 are mixed.'
|
98
|
+
end
|
99
|
+
sum += pubkey
|
100
|
+
end
|
101
|
+
sum
|
65
102
|
end
|
66
103
|
|
67
104
|
# Aggregate multiple signatures.
|
68
105
|
# e(G, S) = e(G, sum(n)Si) = mul(n)(e(G, Si))
|
69
|
-
# @param [Array[
|
70
|
-
# @return [BLS::PointG2] aggregated signature.
|
106
|
+
# @param [Array[BLS::PointG2]|Array[BLS::PointG2]] signatures multiple signatures.
|
107
|
+
# @return [BLS::PointG2|BLS::PointG1] aggregated signature.
|
71
108
|
def aggregate_signatures(signatures)
|
72
109
|
raise BLS::Error, 'Expected non-empty array.' if signatures.empty?
|
73
110
|
|
74
|
-
|
111
|
+
g2_flag = signatures.first.is_a?(PointG2)
|
112
|
+
sum = g2_flag ? PointG2::ZERO : PointG1::ZERO
|
113
|
+
signatures.each do |signature|
|
114
|
+
if g2_flag && !signature.is_a?(PointG2) || !g2_flag && !signature.is_a?(PointG1)
|
115
|
+
raise BLS::Error, 'Signature G1 and G2 are mixed.'
|
116
|
+
end
|
117
|
+
sum += signature
|
118
|
+
end
|
119
|
+
sum
|
75
120
|
end
|
76
121
|
|
77
122
|
# Verify aggregated signature.
|
78
|
-
# @param [BLS::PointG2] signature aggregated signature.
|
123
|
+
# @param [BLS::PointG2|BLS::PointG1] signature aggregated signature(BLS::PointG2 or BLS::PointG1).
|
79
124
|
# @param [Array[String]] messages the list of message.
|
80
|
-
# @param [Array[
|
125
|
+
# @param [Array[BLS::PointG1]|Array[BLS::PointG2]] public_keys the list of public keys(BLS::PointG1 or BLS::PointG2).
|
81
126
|
# @return [Boolean] verification result.
|
82
127
|
def verify_batch(signature, messages, public_keys)
|
83
128
|
raise BLS::Error, 'Expected non-empty array.' if messages.empty?
|
84
129
|
raise BLS::Error, 'Public keys count should equal msg count.' unless messages.size == public_keys.size
|
85
130
|
|
86
|
-
|
87
|
-
|
131
|
+
sig_g2_flag = signature.is_a?(PointG2)
|
132
|
+
public_keys.each do |public_key|
|
133
|
+
if sig_g2_flag && !public_key.is_a?(PointG1) || !sig_g2_flag && !public_key.is_a?(PointG2)
|
134
|
+
raise BLS::Error, "Public key must be #{sig_g2_flag ? 'PointG1' : 'PointG2'}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
n_message = messages.map { |m| sig_g2_flag ? BLS.norm_p2h(m) : BLS.norm_p1h(m)}
|
88
139
|
paired = []
|
140
|
+
zero = sig_g2_flag ? PointG1::ZERO : PointG2::ZERO
|
89
141
|
n_message.each do |message|
|
90
|
-
group_pubkey = n_message.each_with_index.inject(
|
91
|
-
sub_message == message ? group_pubkey +
|
142
|
+
group_pubkey = n_message.each_with_index.inject(zero)do|group_pubkey, (sub_message, i)|
|
143
|
+
sub_message == message ? group_pubkey + public_keys[i] : group_pubkey
|
92
144
|
end
|
93
|
-
paired << BLS.pairing(group_pubkey, message, with_final_exp: false)
|
145
|
+
paired << (sig_g2_flag ? BLS.pairing(group_pubkey, message, with_final_exp: false) :
|
146
|
+
BLS.pairing(message, group_pubkey, with_final_exp: false))
|
94
147
|
end
|
95
|
-
|
96
|
-
|
97
|
-
product = paired.inject(
|
98
|
-
product.final_exponentiate ==
|
148
|
+
paired << (sig_g2_flag ? BLS.pairing(PointG1::BASE.negate, signature, with_final_exp: false) :
|
149
|
+
BLS.pairing(signature, PointG2::BASE.negate, with_final_exp: false))
|
150
|
+
product = paired.inject(Fp12::ONE) { |a, b| a * b }
|
151
|
+
product.final_exponentiate == Fp12::ONE
|
99
152
|
end
|
100
153
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bls12-381
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shigeyuki Azuchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: h2c
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,9 +75,12 @@ files:
|
|
61
75
|
- lib/bls.rb
|
62
76
|
- lib/bls/curve.rb
|
63
77
|
- lib/bls/field.rb
|
78
|
+
- lib/bls/h2c.rb
|
64
79
|
- lib/bls/math.rb
|
65
80
|
- lib/bls/pairing.rb
|
66
81
|
- lib/bls/point.rb
|
82
|
+
- lib/bls/point/g1.rb
|
83
|
+
- lib/bls/point/g2.rb
|
67
84
|
- lib/bls/version.rb
|
68
85
|
homepage: https://github.com/azuchi/bls12-381
|
69
86
|
licenses:
|