bls12-381 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|