kzg 0.3.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 103890dfa58ad58001cf7c7a6aa1be406dbe8fc669615db0ec4922994055b1bb
4
- data.tar.gz: 38bdb89f1d9b418a550283fc6f83101baea538d6d11bb67fb472d171a2803ecc
3
+ metadata.gz: ed4f0b47a0f3f5b5de96f7cd6a931aa979b6ebc84e48eaf7c696755f7caf990f
4
+ data.tar.gz: ae70fe07a4eb798b08669d0f891b9a83bf0d48f27311d859623ae3b3eaf9fa0c
5
5
  SHA512:
6
- metadata.gz: b6322381d853a0dc272a00f5cafb1ad4f2e7646600a335ea21bb613f58763f73b17ac7d8a710f2cbf2ccf255fea3ee46c75b90d81621e834643bd4fa24a69241
7
- data.tar.gz: dd443099a9c9d1db2ce8c5521e0c3e7c170adc1b87cdb50cefe0607141bfab0e4e8296c6e3e41fc44822f13e9d5e25e2b1d541ce43e17ffadff768e9ddae1f77
6
+ metadata.gz: 3b8a04dadb5dc5260debcff77a7b2982f15434e7f3c610189430d53abee3b8d6ac0be2ee0c46b5f7bda7bda52e1551e62e862decc4fa258491d3596301b0d853
7
+ data.tar.gz: 3ca8ab11a1f10b9644c1580ea2b3fbf0714f33ffb5e8460e85155c59ae8ede6f49c1a50ec11dcbc1b45ab2de90e4718a3f4483adb53db1a1d91273f1e7de6c2c
@@ -9,10 +9,9 @@ jobs:
9
9
  strategy:
10
10
  matrix:
11
11
  ruby:
12
- - '3.0'
13
- - '3.1'
14
- - '3.2'
15
12
  - '3.3'
13
+ - '3.4'
14
+ - '4.0'
16
15
  steps:
17
16
  - uses: actions/checkout@v2
18
17
  - name: Set up Ruby
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rake
3
3
  - rubocop-rspec
4
4
  inherit_gem:
@@ -17,7 +17,5 @@ Style/WhileUntilModifier:
17
17
  Enabled: false
18
18
  Naming/MethodParameterName:
19
19
  Enabled: false
20
- RSpec/FilePath:
21
- Enabled: false
22
20
  Layout/LineLength:
23
21
  Enabled: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.3.0
1
+ ruby-3.4.1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2026-08-11
4
+
5
+ ### Added
6
+
7
+ - `KZG.decode_field_element`, which reads the canonical 32 byte big endian encoding of a
8
+ field element and refuses everything else. `BLS::Fr` reduces modulo the group order, which
9
+ is right for an element built in memory and wrong for one arriving as bytes: it would
10
+ otherwise leave `1` and `1 + BLS::Curve::R` as two spellings of a single claim, either of
11
+ which verifies against the other's proof. This is the scalar counterpart of
12
+ `BLS::PointG1.from_hex`.
13
+ - The EIP-4844 reference vectors for `verify_kzg_proof` are run against
14
+ `KZG::Setting#valid_proof?`, which verifies with the same pairing equation.
15
+
16
+ ### Changed
17
+
18
+ - Require bls12-381 0.4.0, whose `from_hex` refuses a point outside the prime-order subgroup
19
+ and a non-canonical encoding of one.
20
+ - `KZG::Setting#valid_proof?` and `#valid_multi_proof?` answer `false` where they used to
21
+ raise, so that untrusted input cannot turn into an exception: for a proof or a commitment
22
+ at infinity, outside the prime-order subgroup, or of the wrong type, and for an `x` that
23
+ repeats, that does not pair up with `y`, or that the setup is too small to open. They also
24
+ check the subgroup themselves, which only `from_hex` did before and only for callers that
25
+ deserialize from hex.
26
+ - `KZG::Commitment` commits the zero polynomial to the point at infinity rather than to
27
+ `nil`, which `#compute_proof` returns for a constant polynomial.
28
+ - `KZG::Setting.new`, `KZG::Commitment.new`, `KZG::Polynomial#div` and `#multiply` raise
29
+ `KZG::Error` for arguments that used to fail later as arithmetic on a nil.
30
+ - `KZG.setup_params` raises `KZG::Error` for an `n` below 2, which built a setting that could
31
+ verify nothing, and for a secret that is a multiple of the group order.
32
+
3
33
  ## [0.1.0] - 2021-04-11
4
34
 
5
35
  - Initial release
data/Gemfile CHANGED
@@ -14,4 +14,6 @@ gem 'prettier', '4.0.3'
14
14
  gem 'rubocop-rake'
15
15
  gem 'rubocop-rspec'
16
16
 
17
- gem 'parallel_tests'
17
+ gem 'parallel_tests'
18
+
19
+ gem 'ostruct'
data/README.md CHANGED
@@ -95,6 +95,35 @@ y = [55, 9217, 280483]
95
95
  setting.valid_multi_proof?(commitment.value, multi_proof, x, y)
96
96
  ```
97
97
 
98
+ #### Untrusted input
99
+
100
+ `valid_proof?` and `valid_multi_proof?` answer `false` for anything they will not accept, so
101
+ a commitment or a proof that arrives over the wire cannot turn into an exception. Both check
102
+ that the points they are given are in the prime-order subgroup, which being on the curve does
103
+ not imply and which proofs are forgeable without.
104
+
105
+ What they cannot check is how `x` and `y` were spelled, because they never see it. `BLS::Fr`
106
+ reduces modulo the group order, which is what makes `-6` and `r - 6` one element, and also
107
+ what leaves `1` and `1 + BLS::Curve::R` as two encodings of a single claim: a proof for
108
+ either verifies against the other. Decode field elements that arrive as bytes with
109
+ `KZG.decode_field_element`, which refuses everything but the canonical 32 byte big endian
110
+ form, in the same way `BLS::PointG1.from_hex` refuses a non-canonical point:
111
+
112
+ ```ruby
113
+ x = KZG.decode_field_element(x_hex) # 32 bytes, big endian, no 0x prefix
114
+ y = KZG.decode_field_element(y_hex) # raises KZG::Error if x_hex is not canonical
115
+ commitment = BLS::PointG1.from_hex(commitment_hex)
116
+ proof = BLS::PointG1.from_hex(proof_hex)
117
+
118
+ setting.valid_proof?(commitment, proof, x, y)
119
+ ```
120
+
121
+ The [EIP-4844 reference vectors for `verify_kzg_proof`][eip4844-vectors] are run against
122
+ `valid_proof?` in the specs; that function verifies with the same pairing equation and reads
123
+ nothing from the trusted setup but `[s]_2.`
124
+
125
+ [eip4844-vectors]: https://github.com/ethereum/c-kzg-4844/tree/main/tests/verify_kzg_proof/kzg-mainnet
126
+
98
127
  ### Use as vector commitment
99
128
 
100
129
  When used as a Vector commitment, the value to be committed is encoded in a polynomial expression as the evaluated value of the polynomial.
@@ -117,3 +146,25 @@ proof = commitment.compute_proof(3)
117
146
  # verify
118
147
  setting.valid_proof?(commitment.value, proof, 3, 9)
119
148
  ```
149
+
150
+ ## Side channels
151
+
152
+ **Committing is not safe against an attacker who can measure it.** A commitment multiplies a
153
+ setup point by each coefficient of the polynomial, and those coefficients are the very thing
154
+ the commitment hides — the vector above among them. The multiplication underneath is
155
+ [not constant time][bls-side-channels]: the scalar decides which precomputed point each
156
+ window adds and so which memory is touched, point addition returns early on the identity, and
157
+ Ruby's bignum arithmetic takes time that depends on its operands.
158
+
159
+ A pure Ruby implementation cannot fix this, and neither can the library underneath. Treat
160
+ committing as suitable where it cannot be observed. Verifying is fine either way, since it
161
+ touches nothing secret.
162
+
163
+ [bls-side-channels]: https://github.com/azuchi/bls12-381#side-channels
164
+
165
+ ## Threads
166
+
167
+ Verifying from several threads gives the right answers. `valid_proof?` pairs against
168
+ `BLS::PointG2::BASE`, which memoises its pairing coefficients on first use without a lock, so
169
+ racing callers can each compute them; what is assigned is always a finished array, so the
170
+ cost is the repeated work rather than a wrong result.
data/kzg.gemspec CHANGED
@@ -28,10 +28,10 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ['lib']
29
29
 
30
30
  # Uncomment to register a new dependency of your gem
31
- spec.add_dependency 'bls12-381', '~> 0.3.0'
31
+ spec.add_dependency 'bigdecimal'
32
+ spec.add_dependency 'bls12-381', '~> 0.4.0'
32
33
 
33
34
  # For more information and examples about making a new gem, checkout our
34
35
  # guide at: https://bundler.io/guides/creating_gem.html
35
36
  spec.add_development_dependency 'bundler'
36
- spec.add_development_dependency 'rake', '>= 12.3.3'
37
37
  end
@@ -8,9 +8,22 @@ module KZG
8
8
  # Create commitment
9
9
  # @param [KZG::Setting] setting
10
10
  # @param [KZG::Polynomial] polynomial
11
+ # @raise [KZG::Error] Occur when the polynomial has more coefficients than the setup has
12
+ # points to commit them with.
11
13
  def initialize(setting, polynomial)
14
+ # Checked here rather than only in from_coeffs, which is not the only way in: the
15
+ # verification path builds its interpolation straight through new, and a coefficient
16
+ # with no point to meet is otherwise a nil that fails as arithmetic.
17
+ if polynomial.coeffs.length > setting.g1_points.length
18
+ raise KZG::Error,
19
+ "coeffs length is greater than the number of secret parameters."
20
+ end
21
+
12
22
  @setting = setting
13
23
  @polynomial = polynomial
24
+ # Seeded with the point at infinity so that the zero polynomial commits to it rather
25
+ # than to nil. compute_proof reaches here with an empty quotient whenever the
26
+ # polynomial is a constant, and that proof goes on to a pairing.
14
27
  @value =
15
28
  polynomial
16
29
  .coeffs
@@ -19,17 +32,14 @@ module KZG
19
32
  c = c.is_a?(BLS::Fr) ? c : BLS::Fr.new(c)
20
33
  c.value.zero? ? BLS::PointG1::ZERO : setting.g1_points[i] * c
21
34
  end
22
- .inject(&:+)
35
+ .inject(BLS::PointG1::ZERO, &:+)
23
36
  end
24
37
 
25
38
  # Create commitment using coefficients.
26
39
  # @param [KZG::Setting] setting
27
40
  # @param [Array(Integer | BLS::Fr)] coeffs Coefficients of polynomial equation.
41
+ # @raise [KZG::Error] Occur when there are more coefficients than setup points, as in new.
28
42
  def self.from_coeffs(setting, coeffs)
29
- if coeffs.length > setting.g1_points.length
30
- raise KZG::Error,
31
- "coeffs length is greater than the number of secret parameters."
32
- end
33
43
  Commitment.new(setting, KZG::Polynomial.new(coeffs))
34
44
  end
35
45
 
@@ -12,13 +12,27 @@ module KZG
12
12
  end
13
13
 
14
14
  # Create polynomial using lagrange interpolation using (x, y) list.
15
- # @param [Array(Integer)] x The array of x coordinate.
16
- # @param [Array(Integer)] y The array of y coordinate.
15
+ # @param [Array(Integer)] x The array of x coordinate. Must not contain the same value
16
+ # twice: no polynomial passes through two different y at one x, and one that repeats an
17
+ # (x, y) pair is under-determined either way.
18
+ # @param [Array(Integer)] y The array of y coordinate. Must be as long as +x+.
17
19
  # @return [KZG::Polynomial]
20
+ # @raise [KZG::Error] Occur when x contains a duplicate value, or x and y differ in
21
+ # length.
18
22
  def self.lagrange_interpolate(x, y)
19
23
  n = x.length
24
+ unless n == y.length
25
+ raise KZG::Error, "x and y must have the same length."
26
+ end
27
+
20
28
  x = x.map { |i| i.is_a?(BLS::Fr) ? i : BLS::Fr.new(i) }
21
29
  y = y.map { |i| i.is_a?(BLS::Fr) ? i : BLS::Fr.new(i) }
30
+ # The product below runs over the differences between x[i] and every other x, so a
31
+ # repeat makes it zero and the division by it undefined. Caught here rather than left
32
+ # to BLS::Fr#invert so that it reads as the input error it is.
33
+ if x.map(&:value).uniq.length < n
34
+ raise KZG::Error, "x contains duplicate value."
35
+ end
22
36
  coeffs = Array.new(n, BLS::Fr::ZERO)
23
37
  n.times do |i|
24
38
  prod = BLS::Fr::ONE
@@ -40,7 +54,10 @@ module KZG
40
54
  # Create polynomial from array of x coordinate like f(x) = (x - x0)(x - x1)...(x - xn)
41
55
  # @param [Array(Integer)] x An array of x coordinate.
42
56
  # @return [KZG::Polynomial]
57
+ # @raise [KZG::Error] Occur when x is empty, which names no roots to build from.
43
58
  def self.zero_poly(x)
59
+ raise KZG::Error, "x must not be empty." if x.empty?
60
+
44
61
  poleis =
45
62
  x.map { |v| Polynomial.new([BLS::Fr.new(v).negate, BLS::Fr::ONE]) }
46
63
  poleis[1..].inject(poleis.first) { |result, poly| result * poly }
@@ -53,7 +70,7 @@ module KZG
53
70
  x = x.is_a?(BLS::Fr) ? x : BLS::Fr.new(x)
54
71
  return BLS::Fr::ZERO if coeffs.empty?
55
72
  return coeffs.first if x.value.zero?
56
- last = coeffs[coeffs.length - 1]
73
+ last = coeffs.last
57
74
  (coeffs.length - 2).step(0, -1) do |i|
58
75
  tmp = last * x
59
76
  last = tmp + coeffs[i]
@@ -96,6 +113,11 @@ module KZG
96
113
  unless other.is_a?(Polynomial)
97
114
  raise ArgumentError, "multiply target must be Polynomial"
98
115
  end
116
+ # An empty coefficient list is the zero polynomial, and zero times anything is zero.
117
+ # Left to the loop below, an empty operand sizes the result at -1 when both are empty,
118
+ # and otherwise leaves it a row of nils, since the inner loop never runs to fill them.
119
+ return Polynomial.new([]) if coeffs.empty? || other.coeffs.empty?
120
+
99
121
  new_coeffs = Array.new(coeffs.length + other.coeffs.length - 1)
100
122
  coeffs.each.with_index do |a, i|
101
123
  other.coeffs.each.with_index do |b, j|
@@ -111,10 +133,18 @@ module KZG
111
133
  # @param [KZG::Polynomial] other Other polynomial
112
134
  # @return [KZG::Polynomial] Divided polynomial
113
135
  # @return ArgumentError
136
+ # @raise [KZG::Error] Occur when +other+ has no non-zero leading coefficient to divide by.
114
137
  def div(other)
115
138
  unless other.is_a?(Polynomial)
116
139
  raise ArgumentError, "divide target must be Polynomial"
117
140
  end
141
+ # The long division below divides by this coefficient once per step. Zero has no
142
+ # inverse, and an empty divisor has no coefficient at all; neither denominator names a
143
+ # polynomial of any degree, so refuse both here rather than reach BLS::Fr#invert.
144
+ if other.coeffs.empty? || other.coeffs.last.value.zero?
145
+ raise KZG::Error, "divisor must have a non-zero leading coefficient."
146
+ end
147
+
118
148
  a = coeffs.dup
119
149
  a_pos = a.length - 1
120
150
  b_pos = other.coeffs.length - 1
data/lib/kzg/setting.rb CHANGED
@@ -6,21 +6,33 @@ module KZG
6
6
  attr_reader :g1_points, :g2_points
7
7
 
8
8
  # @param [Array(BLS::PointG1)] g1_points
9
- # @param [Array(BLS::PointG2)] g2_points
9
+ # @param [Array(BLS::PointG2)] g2_points at least [1]_2 and [s]_2, the two a proof is
10
+ # checked against.
11
+ # @raise [KZG::Error] Occur when either array holds something other than the points of
12
+ # its group, or when g2_points is too short to verify with.
10
13
  def initialize(g1_points, g2_points)
11
14
  raise KZG::Error, "g1_points must be array." unless g1_points.is_a?(Array)
15
+ raise KZG::Error, "g2_points must be array." unless g2_points.is_a?(Array)
12
16
  unless g1_points.all? { |g| g.is_a?(BLS::PointG1) }
13
17
  raise KZG::Error, "All elements of g1_points must be BLS::PointG1."
14
18
  end
15
19
  unless g2_points.all? { |g| g.is_a?(BLS::PointG2) }
16
20
  raise KZG::Error, "All elements of g2_points must be BLS::PointG2."
17
21
  end
22
+ # valid_proof? reads g2_points[1], so a setting short of it can verify nothing. Refused
23
+ # here rather than at verification time, where it is a nil that has travelled some way
24
+ # from the setup that omitted it.
25
+ if g2_points.length < 2
26
+ raise KZG::Error, "g2_points must have at least 2 elements."
27
+ end
18
28
 
19
29
  @g1_points = g1_points
20
30
  @g2_points = g2_points
21
31
  end
22
32
 
23
33
  def ==(other)
34
+ return false unless other.is_a?(Setting)
35
+
24
36
  g1_points == other.g1_points && g2_points == other.g2_points
25
37
  end
26
38
 
@@ -29,20 +41,22 @@ module KZG
29
41
  # @param [BLS::PointG1] proof
30
42
  # @param [Integer|BLS::Fr] x
31
43
  # @param [Integer|BLS::Fr] y
44
+ # @return [Boolean] verification result.
32
45
  def valid_proof?(commit_point, proof, x, y)
46
+ return false unless g1_element?(commit_point) && g1_element?(proof)
47
+
33
48
  x = x.is_a?(BLS::Fr) ? x : BLS::Fr.new(x)
34
49
  y = y.is_a?(BLS::Fr) ? y : BLS::Fr.new(y)
35
50
  xg2 = x.value.zero? ? BLS::PointG2::ZERO : BLS::PointG2::BASE * x
36
51
  yg = y.value.zero? ? BLS::PointG1::ZERO : BLS::PointG1::BASE * y
37
52
 
38
53
  # e([commitment - y]^(-1), [1]) * e([proof], [s - x]) = 1
39
- lhs =
40
- BLS.pairing(
41
- (commit_point - yg).negate,
42
- BLS::PointG2::BASE,
43
- with_final_exp: false
44
- )
45
- rhs = BLS.pairing(proof, g2_points[1] - xg2, with_final_exp: false)
54
+ # partial_pairing rather than pairing: a proof at infinity, or a commitment that
55
+ # cancels with [y], is a degenerate input a verifier can be handed and has to come out
56
+ # as false rather than as a BLS::PairingError. e(O, Q) = e(P, O) = 1 leaves the
57
+ # equation above intact.
58
+ lhs = BLS.partial_pairing((commit_point - yg).negate, BLS::PointG2::BASE)
59
+ rhs = BLS.partial_pairing(proof, g2_points[1] - xg2)
46
60
  exp = (lhs * rhs).final_exponentiate
47
61
  exp == BLS::Fp12::ONE
48
62
  end
@@ -52,21 +66,36 @@ module KZG
52
66
  # @param [BLS::PointG1] proof
53
67
  # @param [Array(Integer|BLS::Fr)] x
54
68
  # @param [Array(Integer|BLS::Fr)] y
69
+ # @return [Boolean] verification result. A claim this method cannot interpolate through
70
+ # is a claim that does not verify, so a malformed x/y is false rather than an exception.
55
71
  def valid_multi_proof?(commit_point, proof, x, y)
72
+ return false unless g1_element?(commit_point) && g1_element?(proof)
73
+ # Nothing is claimed over an empty set of points, and a y that does not pair up with an
74
+ # x leaves the interpolation below indexing past the end of it.
75
+ return false if x.empty? || x.length != y.length
76
+ # Both commitments below take one setup point per coefficient, and z(x) is of degree
77
+ # x.length, so it needs one G2 point more than there are x. A claim larger than that is
78
+ # one this setting cannot weigh, rather than one it has weighed and rejected, but it
79
+ # reaches here from whoever is being verified and so cannot raise either.
80
+ if x.length > g1_points.length || x.length >= g2_points.length
81
+ return false
82
+ end
83
+
56
84
  x = x.map { |v| v.is_a?(BLS::Fr) ? v.value : v }
57
85
  y = y.map { |v| v.is_a?(BLS::Fr) ? v.value : v }
86
+ # Interpolation through a repeated x is undefined, and lagrange_interpolate raises on
87
+ # it. The x here comes from whoever the proof is being verified for, so a repeat has to
88
+ # be reported as a failed proof instead of letting the exception escape. Compared after
89
+ # reduction, since x and x + BLS::Curve::R are the same field element.
90
+ return false if x.map { |v| BLS::Fr.new(v).value }.uniq.length < x.length
91
+
58
92
  # compute i(x)
59
93
  i_poly = Polynomial.lagrange_interpolate(x, y)
60
94
  # compute z(x)
61
95
  z_poly = Polynomial.zero_poly(x)
62
96
  # e([commitment - interpolation_polynomial(s)]^(-1), [1]) * e([proof], [s^n - x^n]) = 1
63
97
  is = Commitment.new(self, i_poly).value
64
- lhs =
65
- BLS.pairing(
66
- (commit_point - is).negate,
67
- BLS::PointG2::BASE,
68
- with_final_exp: false
69
- )
98
+ lhs = BLS.partial_pairing((commit_point - is).negate, BLS::PointG2::BASE)
70
99
  z_commit =
71
100
  z_poly
72
101
  .coeffs
@@ -74,10 +103,35 @@ module KZG
74
103
  .with_index do |c, i|
75
104
  c.value.zero? ? BLS::PointG2::ZERO : g2_points[i] * c
76
105
  end
77
- .inject(&:+)
78
- rhs = BLS.pairing(proof, z_commit, with_final_exp: false)
106
+ .inject(BLS::PointG2::ZERO, &:+)
107
+ rhs = BLS.partial_pairing(proof, z_commit)
79
108
  exp = (lhs * rhs).final_exponentiate
80
109
  exp == BLS::Fp12::ONE
81
110
  end
111
+
112
+ private
113
+
114
+ # Whether +point+ is an element of G1, rather than merely a point on the curve over Fp.
115
+ #
116
+ # The subgroup check is what carries weight here. E(Fp) is bigger than the prime-order
117
+ # subgroup by the cofactor, and a point taken from the rest of it makes the pairing
118
+ # bilinear over a group the soundness argument never covered, which is what lets forged
119
+ # proofs through. BLS::PointG1.from_hex runs this check as of bls12-381 0.4.0, but only
120
+ # for callers that deserialize their points from hex; a commitment or a proof reaching
121
+ # this class by any other route has never been checked.
122
+ #
123
+ # The curve check has to come first: multiplying a point that satisfies no curve equation
124
+ # is meaningless, and its result landing on infinity would pass the subgroup check on an
125
+ # input that is not a point at all.
126
+ # @param [Object] point
127
+ # @return [Boolean]
128
+ def g1_element?(point)
129
+ return false unless point.is_a?(BLS::PointG1)
130
+
131
+ point.validate! # raises unless on the curve over Fp
132
+ point.in_group?
133
+ rescue BLS::PointError
134
+ false
135
+ end
82
136
  end
83
137
  end
data/lib/kzg/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KZG
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/kzg.rb CHANGED
@@ -12,18 +12,65 @@ module KZG
12
12
  autoload :Polynomial, "kzg/polynomial"
13
13
  autoload :Commitment, "kzg/commitment"
14
14
 
15
+ # Size of the canonical encoding of a field element, as used by EIP-4844 and its test
16
+ # vectors: 32 bytes, big endian.
17
+ FIELD_ELEMENT_BYTE_SIZE = 32
18
+ FIELD_ELEMENT_HEX =
19
+ /\A(?:[0-9a-fA-F]{2}){#{FIELD_ELEMENT_BYTE_SIZE}}\z/.freeze
20
+
15
21
  module_function
16
22
 
23
+ # Decode a field element from its canonical representation.
24
+ #
25
+ # BLS::Fr.new reduces whatever it is handed modulo the group order. That is the right
26
+ # answer for an element built in memory, where -6 and r - 6 are two ways of writing one
27
+ # element, and the wrong one for an element arriving as bytes: it leaves 1 and r + 1 as two
28
+ # spellings of a single claim, and a proof for either verifies against the other. Anything
29
+ # that deduplicates, indexes or compares by the encoding then sees two claims where there
30
+ # is one.
31
+ #
32
+ # This is the scalar counterpart of BLS::PointG1.from_hex, which refuses a non-canonical
33
+ # point for the same reason. Together they cover both halves of the wire format; neither
34
+ # KZG::Setting#valid_proof? nor BLS::Fr can, since neither is handed the encoding.
35
+ #
36
+ # @param [String] hex a field element, 32 bytes big endian in hex, with no 0x prefix.
37
+ # @return [BLS::Fr] the decoded field element.
38
+ # @raise [KZG::Error] Occur when +hex+ is not 32 bytes of hex digits, or encodes a value at
39
+ # or above the group order.
40
+ def decode_field_element(hex)
41
+ unless hex.is_a?(String) && hex.match?(FIELD_ELEMENT_HEX)
42
+ raise KZG::Error,
43
+ "Field element must be #{FIELD_ELEMENT_BYTE_SIZE} bytes in hex."
44
+ end
45
+
46
+ value = hex.to_i(16)
47
+ unless value < BLS::Curve::R
48
+ raise KZG::Error, "Field element must be less than the group order."
49
+ end
50
+
51
+ BLS::Fr.new(value)
52
+ end
53
+
17
54
  # Setup elements of elliptic curve from +secret+.
18
55
  # Note: Since the random secret must not be known to anyone,
19
56
  # this Trusted Setup usually needs to be performed using an MPC or similar.
20
57
  # @param [Integer] secret random secret.
21
- # @param [Integer] n number of parameters.
58
+ # @param [Integer] n number of parameters. At least 2, since a setting needs [1]_2 and
59
+ # [s]_2 to check a proof against.
22
60
  # @return [KZG::Setting]
61
+ # @raise [KZG::Error] Occur when n is below 2, or when the secret reduces to zero.
23
62
  def setup_params(secret, n)
63
+ raise KZG::Error, "n must be 2 or more." unless n.is_a?(Integer) && n >= 2
64
+
65
+ s = BLS::Fr.new(secret)
66
+ # A secret of zero, or any multiple of the group order, leaves every point above [s^0] at
67
+ # infinity: a setup that commits to nothing but the constant term.
68
+ if s.zero?
69
+ raise KZG::Error, "secret must not be a multiple of the group order."
70
+ end
71
+
24
72
  s1 = Array.new(n)
25
73
  s2 = Array.new(n)
26
- s = BLS::Fr.new(secret)
27
74
  s_pow = BLS::Fr::ONE
28
75
  n.times do |i|
29
76
  s1[i] = BLS::PointG1::BASE * s_pow
metadata CHANGED
@@ -1,57 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kzg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-01-11 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: bls12-381
13
+ name: bigdecimal
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - "~>"
16
+ - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: 0.3.0
18
+ version: '0'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
- - - "~>"
23
+ - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: 0.3.0
25
+ version: '0'
27
26
  - !ruby/object:Gem::Dependency
28
- name: bundler
27
+ name: bls12-381
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
- - - ">="
30
+ - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
32
+ version: 0.4.0
33
+ type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
- - - ">="
37
+ - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: '0'
39
+ version: 0.4.0
41
40
  - !ruby/object:Gem::Dependency
42
- name: rake
41
+ name: bundler
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ">="
46
45
  - !ruby/object:Gem::Version
47
- version: 12.3.3
46
+ version: '0'
48
47
  type: :development
49
48
  prerelease: false
50
49
  version_requirements: !ruby/object:Gem::Requirement
51
50
  requirements:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
- version: 12.3.3
53
+ version: '0'
55
54
  description: KZG polynomial commitment library for Ruby.
56
55
  email:
57
56
  - azuchi@chaintope.com
@@ -87,7 +86,6 @@ metadata:
87
86
  homepage_uri: https://github.com/azuchi/kzg
88
87
  source_code_uri: https://github.com/azuchi/kzg
89
88
  changelog_uri: https://github.com/azuchi/kzg
90
- post_install_message:
91
89
  rdoc_options: []
92
90
  require_paths:
93
91
  - lib
@@ -102,8 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
100
  - !ruby/object:Gem::Version
103
101
  version: '0'
104
102
  requirements: []
105
- rubygems_version: 3.5.3
106
- signing_key:
103
+ rubygems_version: 3.6.9
107
104
  specification_version: 4
108
105
  summary: KZG polynomial commitment library for Ruby.
109
106
  test_files: []