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.
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 [Array[Integer]] byte array.
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::Fq] private key field.
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
- fq = Fq.new(k)
131
- raise BLS::Error, 'Private key cannot be 0' if fq.zero?
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::Fq12]
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)
@@ -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
@@ -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