bls12-381 0.2.1 → 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/.github/workflows/main.yml +9 -1
- data/README.md +5 -3
- data/bls12-381.gemspec +1 -1
- data/lib/bls/field.rb +172 -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/h2c.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module BLS
|
2
|
+
module H2C
|
3
|
+
|
4
|
+
LENGTH = 64
|
5
|
+
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# @param [String] message hash value with hex format.
|
9
|
+
# @param [Integer] len_in_bytes length
|
10
|
+
# @return [Array[Integer]] byte array.
|
11
|
+
# @raise BLS::Error
|
12
|
+
def expand_message_xmd(message, len_in_bytes)
|
13
|
+
b_in_bytes = BigDecimal(SHA256_DIGEST_SIZE)
|
14
|
+
r_in_bytes = b_in_bytes * 2
|
15
|
+
ell = (BigDecimal(len_in_bytes) / b_in_bytes).ceil
|
16
|
+
raise BLS::Error, 'Invalid xmd length' if ell > 255
|
17
|
+
|
18
|
+
dst_prime = PointG2::DST_BASIC.bytes + BLS.i2osp(PointG2::DST_BASIC.bytesize, 1)
|
19
|
+
z_pad = BLS.i2osp(0, r_in_bytes)
|
20
|
+
l_i_b_str = BLS.i2osp(len_in_bytes, 2)
|
21
|
+
b = Array.new(ell)
|
22
|
+
payload = z_pad + [message].pack('H*').bytes + l_i_b_str + BLS.i2osp(0, 1) + dst_prime
|
23
|
+
b_0 = Digest::SHA256.digest(payload.pack('C*'))
|
24
|
+
b[0] = Digest::SHA256.digest((b_0.bytes + BLS.i2osp(1, 1) + dst_prime).pack('C*'))
|
25
|
+
(1..ell).each do |i|
|
26
|
+
args = BLS.bin_xor(b_0, b[i - 1]).bytes + BLS.i2osp(i + 1, 1) + dst_prime
|
27
|
+
b[i] = Digest::SHA256.digest(args.pack('C*'))
|
28
|
+
end
|
29
|
+
b.map(&:bytes).flatten[0...len_in_bytes]
|
30
|
+
end
|
31
|
+
|
32
|
+
module G2
|
33
|
+
module_function
|
34
|
+
|
35
|
+
# Convert hash to Field.
|
36
|
+
# @param [String] message hash value with hex format.
|
37
|
+
# @return [Array[Integer]] byte array.
|
38
|
+
def hash_to_field(message, random_oracle: true)
|
39
|
+
degree = 2
|
40
|
+
count = random_oracle ? 2 : 1
|
41
|
+
len_in_bytes = count * degree * LENGTH
|
42
|
+
pseudo_random_bytes = BLS::H2C.expand_message_xmd(message, len_in_bytes)
|
43
|
+
u = Array.new(count)
|
44
|
+
count.times do |i|
|
45
|
+
e = Array.new(degree)
|
46
|
+
degree.times do |j|
|
47
|
+
elm_offset = LENGTH * (j + i * degree)
|
48
|
+
tv = pseudo_random_bytes[elm_offset...(elm_offset + LENGTH)]
|
49
|
+
e[j] = BLS.mod(BLS.os2ip(tv), Curve::P)
|
50
|
+
end
|
51
|
+
u[i] = e
|
52
|
+
end
|
53
|
+
u
|
54
|
+
end
|
55
|
+
|
56
|
+
# Optimized SWU Map - Fp2 to G2': y^2 = x^3 + 240i * x + 1012 + 1012i
|
57
|
+
def map_to_curve_sswu(t)
|
58
|
+
iso_3_a = Fp2.new([0, 240])
|
59
|
+
iso_3_b = Fp2.new([1012, 1012])
|
60
|
+
iso_3_z = Fp2.new([-2, -1])
|
61
|
+
t = Fp2.new(t) if t.is_a?(Array)
|
62
|
+
t2 = t**2
|
63
|
+
iso_3_z_t2 = iso_3_z * t2
|
64
|
+
ztzt = iso_3_z_t2 + iso_3_z_t2**2
|
65
|
+
denominator = (iso_3_a * ztzt).negate
|
66
|
+
numerator = iso_3_b * (ztzt + Fp2::ONE)
|
67
|
+
denominator = iso_3_z * iso_3_a if denominator.zero?
|
68
|
+
v = denominator**3
|
69
|
+
u = numerator**3 + iso_3_a * numerator * denominator**2 + iso_3_b * v
|
70
|
+
success, sqrt_candidate_or_gamma = BLS.sqrt_div_fp2(u, v)
|
71
|
+
y = success ? sqrt_candidate_or_gamma : nil
|
72
|
+
sqrt_candidate_x1 = sqrt_candidate_or_gamma * t**3
|
73
|
+
u = iso_3_z_t2**3 * u
|
74
|
+
success2 = false
|
75
|
+
Fp2::ETAS.each do |eta|
|
76
|
+
eta_sqrt_candidate = eta * sqrt_candidate_x1
|
77
|
+
temp = eta_sqrt_candidate**2 * v - u
|
78
|
+
if temp.zero? && !success && !success2
|
79
|
+
y = eta_sqrt_candidate
|
80
|
+
success2 = true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
raise BLS::PointError, 'Hash to Curve - Optimized SWU failure' if !success && !success2
|
84
|
+
|
85
|
+
numerator *= iso_3_z_t2 if success2
|
86
|
+
y = y.negate if BLS.sgn0(t) != BLS.sgn0(y)
|
87
|
+
y *= denominator
|
88
|
+
[numerator, y, denominator]
|
89
|
+
end
|
90
|
+
|
91
|
+
# 3-isogeny map from E' to E
|
92
|
+
# Converts from Jacobi (xyz) to Projective (xyz) coordinates.
|
93
|
+
def isogeny_map(x, y, z)
|
94
|
+
mapped = Array.new(4, Fp2::ZERO)
|
95
|
+
z_powers = [z, z**2, z**3]
|
96
|
+
ISOGENY_COEFFICIENTS.each.with_index do |k, i|
|
97
|
+
mapped[i] = k[-1]
|
98
|
+
arr = k[0...-1].reverse
|
99
|
+
arr.each.with_index do |a, j|
|
100
|
+
mapped[i] = mapped[i] * x + z_powers[j] * a
|
101
|
+
end
|
102
|
+
end
|
103
|
+
mapped[2] *= y
|
104
|
+
mapped[3] *= z
|
105
|
+
z2 = mapped[1] * mapped[3]
|
106
|
+
x2 = mapped[0] * mapped[3]
|
107
|
+
y2 = mapped[1] * mapped[2]
|
108
|
+
[x2, y2, z2]
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
data/lib/bls/math.rb
CHANGED
@@ -25,7 +25,7 @@ module BLS
|
|
25
25
|
|
26
26
|
# Convert byte to non-negative integer.
|
27
27
|
# @param [Array[Integer]] bytes byte array.
|
28
|
-
# @return [
|
28
|
+
# @return [Integer] Integer.
|
29
29
|
def os2ip(bytes)
|
30
30
|
res = 0
|
31
31
|
bytes.each do |b|
|
@@ -66,71 +66,14 @@ module BLS
|
|
66
66
|
res.pack('C*')
|
67
67
|
end
|
68
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
69
|
# Normalize private key.
|
125
70
|
# @param [String|Integer] private_key a private key with hex or number.
|
126
|
-
# @return [BLS::
|
71
|
+
# @return [BLS::Fr] Normalized private key.
|
127
72
|
# @raise [BLS::Error] Occur when the private key is zero.
|
128
73
|
def normalize_priv_key(private_key)
|
129
74
|
k = private_key.is_a?(String) ? private_key.to_i(16) : private_key
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
fq
|
75
|
+
raise BLS::Error, "Private key must be 0 < private_key < #{Fr::ORDER}" if k <= 0
|
76
|
+
Fr.new(k)
|
134
77
|
end
|
135
78
|
|
136
79
|
# Convert number to +byte_length+ bytes hex string.
|
data/lib/bls/pairing.rb
CHANGED
@@ -6,7 +6,7 @@ module BLS
|
|
6
6
|
# @param [BLS::PointG1] p
|
7
7
|
# @param [BLS::PointG2] q
|
8
8
|
# @param [Boolean] with_final_exp
|
9
|
-
# @return [BLS::
|
9
|
+
# @return [BLS::Fp12]
|
10
10
|
# @raise [BLS::PairingError] Occur when p.zero? or q.zero?
|
11
11
|
# @raise [ArgumentError]
|
12
12
|
def pairing(p, q, with_final_exp: true)
|
data/lib/bls/point/g1.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
module BLS
|
2
|
+
class PointG1 < BLS::ProjectivePoint
|
3
|
+
|
4
|
+
DST_BASIC = 'BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_'
|
5
|
+
|
6
|
+
KEY_SIZE_COMPRESSED = 48
|
7
|
+
KEY_SIZE_UNCOMPRESSED = 96
|
8
|
+
|
9
|
+
BASE = PointG1.new(Fp.new(Curve::G_X), Fp.new(Curve::G_Y), Fp::ONE)
|
10
|
+
ZERO = PointG1.new(Fp::ONE, Fp::ONE, Fp::ZERO)
|
11
|
+
MAX_BITS = Fp::MAX_BITS
|
12
|
+
|
13
|
+
# Parse PointG1 from form hex.
|
14
|
+
# @param [String] hex hex value of PointG1.
|
15
|
+
# @return [PointG1]
|
16
|
+
# @raise [BLS::PointError] Occurs when hex length does not match, or point does not on G1.
|
17
|
+
def self.from_hex(hex)
|
18
|
+
bytes = [hex].pack('H*')
|
19
|
+
point = case bytes.bytesize
|
20
|
+
when KEY_SIZE_COMPRESSED
|
21
|
+
compressed_value = hex.to_i(16)
|
22
|
+
b_flag = BLS.mod(compressed_value, POW_2_383) / POW_2_382
|
23
|
+
return ZERO if b_flag == 1
|
24
|
+
|
25
|
+
x = BLS.mod(compressed_value, POW_2_381)
|
26
|
+
full_y = BLS.mod(x**3 + Fp.new(Curve::B).value, Curve::P)
|
27
|
+
y = BLS.pow_mod(full_y, (Curve::P + 1) / 4, Curve::P)
|
28
|
+
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?
|
29
|
+
|
30
|
+
a_flag = BLS.mod(compressed_value, POW_2_382) / POW_2_381
|
31
|
+
y = Curve::P - y unless ((y * 2) / Curve::P) == a_flag
|
32
|
+
PointG1.new(Fp.new(x), Fp.new(y), Fp::ONE)
|
33
|
+
when KEY_SIZE_UNCOMPRESSED
|
34
|
+
return ZERO unless (bytes[0].unpack1('H*').to_i(16) & (1 << 6)).zero?
|
35
|
+
|
36
|
+
x = bytes[0...PUBLIC_KEY_LENGTH].unpack1('H*').to_i(16)
|
37
|
+
y = bytes[PUBLIC_KEY_LENGTH..-1].unpack1('H*').to_i(16)
|
38
|
+
PointG1.new(Fp.new(x), Fp.new(y), Fp::ONE)
|
39
|
+
else
|
40
|
+
raise PointError, 'Invalid point G1, expected 48 or 96 bytes.'
|
41
|
+
end
|
42
|
+
point.validate!
|
43
|
+
point
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_hex(compressed: false)
|
47
|
+
if compressed
|
48
|
+
if self == PointG1::ZERO
|
49
|
+
hex = POW_2_383 + POW_2_382
|
50
|
+
else
|
51
|
+
x, y = to_affine
|
52
|
+
flag = (y.value * 2) / Curve::P
|
53
|
+
hex = x.value + flag * POW_2_381 + POW_2_383
|
54
|
+
end
|
55
|
+
BLS.num_to_hex(hex, PUBLIC_KEY_LENGTH)
|
56
|
+
else
|
57
|
+
if self == PointG1::ZERO
|
58
|
+
(1 << 6).to_s(16) + '00' * (2 * PUBLIC_KEY_LENGTH - 1)
|
59
|
+
else
|
60
|
+
x, y = to_affine
|
61
|
+
BLS.num_to_hex(x.value, PUBLIC_KEY_LENGTH) + BLS.num_to_hex(y.value, PUBLIC_KEY_LENGTH)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Parse Point from private key.
|
67
|
+
# @param [String|Integer] private_key a private key with hex or number.
|
68
|
+
# @return [PointG1] G1Point corresponding to private keys.
|
69
|
+
# @raise [BLS::Error] Occur when the private key is zero.
|
70
|
+
def self.from_private_key(private_key)
|
71
|
+
BASE * BLS.normalize_priv_key(private_key)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Convert hash to PointG1
|
75
|
+
# @param [String] message a hash with hex format.
|
76
|
+
# @return [BLS::PointG1] point.
|
77
|
+
# @raise [BLS::PointError]
|
78
|
+
def self.hash_to_curve(message)
|
79
|
+
raise PointError, 'expected hex string' unless message[/^[a-fA-F0-9]*$/]
|
80
|
+
|
81
|
+
h2c = ::H2C.get(::H2C::Suite::BLS12381G1_XMDSHA256_SWU_RO_, PointG1::DST_BASIC)
|
82
|
+
p = h2c.digest([message].pack('H*'))
|
83
|
+
|
84
|
+
PointG1.new(Fp.new(p.x), Fp.new(p.y), Fp::ONE)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Validate this point whether on curve over Fp.
|
88
|
+
# @raise [PointError] Occur when this point not on curve over Fp.
|
89
|
+
def validate!
|
90
|
+
b = Fp.new(Curve::B)
|
91
|
+
return if zero?
|
92
|
+
|
93
|
+
left = y.pow(2) * z - x.pow(3)
|
94
|
+
right = b * z.pow(3)
|
95
|
+
raise PointError, 'Invalid point: not on curve over Fp' unless left == right
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sparse multiplication against precomputed coefficients.
|
99
|
+
# @param [PointG2] p
|
100
|
+
def miller_loop(p)
|
101
|
+
BLS.miller_loop(p.pairing_precomputes, to_affine)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/lib/bls/point/g2.rb
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
module BLS
|
2
|
+
class PointG2 < BLS::ProjectivePoint
|
3
|
+
|
4
|
+
attr_accessor :precomputes
|
5
|
+
|
6
|
+
DST_BASIC = 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_'
|
7
|
+
|
8
|
+
KEY_SIZE_COMPRESSED = 96
|
9
|
+
KEY_SIZE_UNCOMPRESSED = 192
|
10
|
+
|
11
|
+
MAX_BITS = Fp2::MAX_BITS
|
12
|
+
BASE = PointG2.new(Fp2.new(Curve::G2_X), Fp2.new(Curve::G2_Y), Fp2::ONE)
|
13
|
+
ZERO = PointG2.new(Fp2::ONE, Fp2::ONE, Fp2::ZERO)
|
14
|
+
|
15
|
+
# Parse PointG2 from form hex.
|
16
|
+
# https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-07#section-appendix.c
|
17
|
+
# @param [String] hex hex value of PointG2. Currently, only uncompressed formats(196 bytes) are supported.
|
18
|
+
# @return [BLS::PointG2] PointG2.
|
19
|
+
# @raise [BLS::PointError]
|
20
|
+
def self.from_hex(hex)
|
21
|
+
bytes = [hex].pack('H*')
|
22
|
+
m_byte = bytes[0].unpack1('C')& 0xe0
|
23
|
+
if [0x20, 0x60, 0xe0].include?(m_byte)
|
24
|
+
raise PointError, "Invalid encoding flag: #{m_byte.to_s(16)}"
|
25
|
+
end
|
26
|
+
|
27
|
+
c_bit = m_byte & POINT_COMPRESSION_FLAG # compression flag
|
28
|
+
i_bit = m_byte & POINT_INFINITY_FLAG # infinity flag
|
29
|
+
s_bit = m_byte & POINT_Y_FLAG # y coordinate sign flag
|
30
|
+
bytes[0] = [bytes[0].unpack1('C') & 0x1f].pack('C') # set flag to 0
|
31
|
+
|
32
|
+
if i_bit == POINT_INFINITY_FLAG && bytes.unpack1('H*').to_i(16) > 0
|
33
|
+
raise PointError, 'Invalid point, infinity point should be all 0.'
|
34
|
+
end
|
35
|
+
|
36
|
+
point = if bytes.bytesize == KEY_SIZE_COMPRESSED && c_bit == POINT_COMPRESSION_FLAG # compress format
|
37
|
+
return ZERO if i_bit == POINT_INFINITY_FLAG
|
38
|
+
x1 = bytes[0...PUBLIC_KEY_LENGTH].unpack1('H*').to_i(16)
|
39
|
+
x0 = bytes[PUBLIC_KEY_LENGTH...(2 * PUBLIC_KEY_LENGTH)].unpack1('H*').to_i(16)
|
40
|
+
x = Fp2.new([x0, x1])
|
41
|
+
right = x ** 3 + Fp2.new(Curve::B2)
|
42
|
+
y = right.sqrt
|
43
|
+
raise PointError, 'Invalid compressed G2 point' unless y
|
44
|
+
bit_y = if y.coeffs[1].value == 0
|
45
|
+
(y.coeffs[0].value * 2) / Curve::P
|
46
|
+
else
|
47
|
+
(y.coeffs[1].value * 2) / Curve::P == 1 ? 1 : 0
|
48
|
+
end
|
49
|
+
y = s_bit > 0 && bit_y > 0 ? y : y.negate
|
50
|
+
PointG2.new(x, y, Fp2::ONE)
|
51
|
+
elsif bytes.bytesize == KEY_SIZE_UNCOMPRESSED && c_bit != POINT_COMPRESSION_FLAG # uncompressed format
|
52
|
+
return ZERO if i_bit == POINT_INFINITY_FLAG
|
53
|
+
x1 = bytes[0...PUBLIC_KEY_LENGTH].unpack1('H*').to_i(16)
|
54
|
+
x0 = bytes[PUBLIC_KEY_LENGTH...(2 * PUBLIC_KEY_LENGTH)].unpack1('H*').to_i(16)
|
55
|
+
y1 = bytes[(2 * PUBLIC_KEY_LENGTH)...(3 * PUBLIC_KEY_LENGTH)].unpack1('H*').to_i(16)
|
56
|
+
y0 = bytes[(3 * PUBLIC_KEY_LENGTH)..-1].unpack1('H*').to_i(16)
|
57
|
+
PointG2.new(Fp2.new([x0, x1]), Fp2.new([y0, y1]), Fp2::ONE)
|
58
|
+
else
|
59
|
+
raise PointError, 'Invalid point G2, expected 96/192 bytes.'
|
60
|
+
end
|
61
|
+
point.validate!
|
62
|
+
point
|
63
|
+
end
|
64
|
+
|
65
|
+
# Parse Point from private key.
|
66
|
+
# @param [String|Integer] private_key a private key with hex or number.
|
67
|
+
# @return [PointG1] G1Point corresponding to private keys.
|
68
|
+
# @raise [BLS::Error] Occur when the private key is zero.
|
69
|
+
def self.from_private_key(private_key)
|
70
|
+
BASE * BLS.normalize_priv_key(private_key)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Convert hash to PointG2
|
74
|
+
# @param [String] message a hash with hex format.
|
75
|
+
# @return [BLS::PointG2] point.
|
76
|
+
# @raise [BLS::PointError]
|
77
|
+
def self.hash_to_curve(message)
|
78
|
+
raise PointError, 'expected hex string' unless message[/^[a-fA-F0-9]*$/]
|
79
|
+
|
80
|
+
u = BLS::H2C::G2.hash_to_field(message)
|
81
|
+
q0 = PointG2.new(*BLS::H2C::G2.isogeny_map(*BLS::H2C::G2.map_to_curve_sswu(u[0])))
|
82
|
+
q1 = PointG2.new(*BLS::H2C::G2.isogeny_map(*BLS::H2C::G2.map_to_curve_sswu(u[1])))
|
83
|
+
r = q0 + q1
|
84
|
+
clear_cofactor(r)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Serialize pont as hex value.
|
88
|
+
# @param [Boolean] compressed whether to compress the point.
|
89
|
+
# @return [String] hex value of point.
|
90
|
+
def to_hex(compressed: false)
|
91
|
+
if compressed
|
92
|
+
if zero?
|
93
|
+
x1 = POW_2_383 + POW_2_382
|
94
|
+
x0= 0
|
95
|
+
else
|
96
|
+
x, y = to_affine
|
97
|
+
flag = if y.coeffs[1].value == 0
|
98
|
+
(y.coeffs[0].value * 2) / Curve::P
|
99
|
+
else
|
100
|
+
((y.coeffs[1].value * 2) / Curve::P).zero? ? 0 : 1
|
101
|
+
end
|
102
|
+
x1 = x.coeffs[1].value + flag * POW_2_381 + POW_2_383
|
103
|
+
x0 = x.coeffs[0].value
|
104
|
+
end
|
105
|
+
BLS.num_to_hex(x1, PUBLIC_KEY_LENGTH) + BLS.num_to_hex(x0, PUBLIC_KEY_LENGTH)
|
106
|
+
else
|
107
|
+
if self == PointG2::ZERO
|
108
|
+
(1 << 6).to_s(16) + '00' * (4 * PUBLIC_KEY_LENGTH - 1)
|
109
|
+
else
|
110
|
+
validate!
|
111
|
+
x, y = to_affine.map(&:values)
|
112
|
+
BLS.num_to_hex(x[1], PUBLIC_KEY_LENGTH) +
|
113
|
+
BLS.num_to_hex(x[0], PUBLIC_KEY_LENGTH) +
|
114
|
+
BLS.num_to_hex(y[1], PUBLIC_KEY_LENGTH) +
|
115
|
+
BLS.num_to_hex(y[0], PUBLIC_KEY_LENGTH)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Convert to signature with hex format.
|
121
|
+
# @return [String] signature with hex format.
|
122
|
+
# @deprecated Use {#to_hex} instead.
|
123
|
+
def to_signature
|
124
|
+
to_hex(compressed: true)
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate!
|
128
|
+
b = Fp2.new(Curve::B2)
|
129
|
+
return if zero?
|
130
|
+
|
131
|
+
left = y.pow(2) * z - x.pow(3)
|
132
|
+
right = b * z.pow(3)
|
133
|
+
raise PointError, 'Invalid point: not on curve over Fp2' unless left == right
|
134
|
+
end
|
135
|
+
|
136
|
+
def clear_pairing_precomputes
|
137
|
+
self.precomputes = nil
|
138
|
+
end
|
139
|
+
|
140
|
+
def pairing_precomputes
|
141
|
+
return precomputes if precomputes
|
142
|
+
|
143
|
+
self.precomputes = calc_pairing_precomputes(*to_affine)
|
144
|
+
precomputes
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def calc_pairing_precomputes(x, y)
|
150
|
+
q_x, q_y, q_z = [x, y, Fp2::ONE]
|
151
|
+
r_x, r_y, r_z = [q_x, q_y, q_z]
|
152
|
+
ell_coeff = []
|
153
|
+
i = BLS_X_LEN - 2
|
154
|
+
while i >= 0
|
155
|
+
t0 = r_y.square
|
156
|
+
t1 = r_z.square
|
157
|
+
t2 = t1.multiply(3).multiply_by_b
|
158
|
+
t3 = t2 * 3
|
159
|
+
t4 = (r_y + r_z).square - t1 - t0
|
160
|
+
ell_coeff << [t2 - t0, r_x.square * 3, t4.negate]
|
161
|
+
r_x = (t0 - t3) * r_x * r_y / 2
|
162
|
+
r_y = ((t0 + t3) / 2).square - t2.square * 3
|
163
|
+
r_z = t0 * t4
|
164
|
+
unless BLS.bit_get(Curve::X, i).zero?
|
165
|
+
t0 = r_y - q_y * r_z
|
166
|
+
t1 = r_x - q_x * r_z
|
167
|
+
ell_coeff << [t0 * q_x - t1 * q_y, t0.negate, t1]
|
168
|
+
t2 = t1.square
|
169
|
+
t3 = t2 * t1
|
170
|
+
t4 = t2 * r_x
|
171
|
+
t5 = t3 - t4 * 2 + t0.square * r_z
|
172
|
+
r_x = t1 * t5
|
173
|
+
r_y = (t4 - t5) * t0 - t3 * r_y
|
174
|
+
r_z *= t3
|
175
|
+
end
|
176
|
+
i -= 1
|
177
|
+
end
|
178
|
+
ell_coeff
|
179
|
+
end
|
180
|
+
def self.clear_cofactor(p)
|
181
|
+
t1 = p.multiply_unsafe(Curve::X).negate
|
182
|
+
t2 = p.from_affine_tuple(BLS.psi(*p.to_affine))
|
183
|
+
p2 = p.from_affine_tuple(BLS.psi2(*p.double.to_affine))
|
184
|
+
p2 - t2 + (t1 + t2).multiply_unsafe(Curve::X).negate - t1 - p
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|