frostrb 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09af4e893de9e1260635431b1feef84a9eb6273534f0f9e7e2e4bcde82d8788e'
4
- data.tar.gz: 66f39e7bff887c34c65844ac42b94d8e77cdd7fb86caedb575d8ae0a7c0b12a1
3
+ metadata.gz: 0660ec6fbd998b152bdb359276284fd64e00d89ed19e783dbd72e04cac3fad67
4
+ data.tar.gz: c01bd20b7f10d10e0362d6ee4c3722c8849f748ed8899c06efd2cfcaee2dcefa
5
5
  SHA512:
6
- metadata.gz: ba8c33e24ace6174176ec30bb5afaf0222e6c5046df34960ca28c8772286c236611bdb119c3184448fc308f491d1408e57a7fa1c715e3a7e0036e557a6788c46
7
- data.tar.gz: b738d2fe05fa151032e9ff877758f580d6d96153ce179b384a95d6e353ce3e5003638622afbdfd2a4c219825d45f77e78f3e5238484b4cb34de5f8e8de8b768a
6
+ metadata.gz: 5cadf1d9b254ac672aad430dae0c27ec7f45878886bbf600cd250a8c4a405f5d89201de96b5bd145ff3fc59ae2384d227a5fe7e73c3abcf002b36a6fbd2d5098
7
+ data.tar.gz: 71c14b38913bf8471436979fb8d69a99ad7ad763a79d28f1f738435999ad5b485ba7e09d5e43083bbd2edec5ade02c189076e53678c44a4512a71c86e0883a42
data/README.md CHANGED
@@ -85,7 +85,7 @@ round1_outputs = {}
85
85
  # Round 1:
86
86
  # For each participant, perform the first part of the DKG protocol.
87
87
  1.upto(max_signer) do |i|
88
- polynomial, package = FROST::DKG.part1(i, min_signer, max_signer, group)
88
+ polynomial, package = FROST::DKG.generate_secret(i, min_signer, max_signer, group)
89
89
  secrets[i] = polynomial
90
90
  round1_outputs[i] = package
91
91
  end
@@ -93,7 +93,7 @@ end
93
93
  # Each participant sends their commitments and proof to other participants.
94
94
  received_package = {}
95
95
  1.upto(max_signer) do |i|
96
- received_package[i] = round1_outputs.select {|k, _| k != i}.values
96
+ received_package[i] = round1_outputs.select { |k, _| k != i }.values
97
97
  end
98
98
 
99
99
  # Each participant verify knowledge of proof in received package.
@@ -118,7 +118,7 @@ end
118
118
  # Each participant verify received shares.
119
119
  1.upto(max_signer) do |i|
120
120
  received_shares[i].each do |send_by, share|
121
- target_package = received_package[i].find{ |package| package.identifier == send_by }
121
+ target_package = received_package[i].find { |package| package.identifier == send_by }
122
122
  expect(target_package.verify_share(share)).to be true
123
123
  end
124
124
  end
@@ -126,7 +126,7 @@ end
126
126
  # Each participant compute signing share.
127
127
  signing_shares = {}
128
128
  1.upto(max_signer) do |i|
129
- shares = received_shares[i].map{|_, share| share}
129
+ shares = received_shares[i].map { |_, share| share }
130
130
  signing_shares[i] = FROST::DKG.compute_signing_share(secrets[i], shares)
131
131
  end
132
132
 
@@ -134,4 +134,43 @@ end
134
134
  group_pubkey = FROST::DKG.compute_group_pubkey(secrets[1], received_package[1])
135
135
 
136
136
  # The subsequent signing phase is the same as above with signing_shares as the secret.
137
- ```
137
+ ```
138
+
139
+ ### Share repair
140
+
141
+ Using `FROST::Repairable` module, you can repair existing (or new) participant's share with the cooperation of T participants.
142
+
143
+ ```ruby
144
+ # Dealer generate shares.
145
+ FROST::SigningKey.generate(ECDSA::Group::Secp256k1)
146
+ polynomial = dealer.gen_poly(min_signers - 1)
147
+ shares = 1.upto(max_signers).map {|identifier| polynomial.gen_share(identifier) }
148
+
149
+ # Signer 2 will lose their share
150
+ # Signers (helpers) 1, 4 and 5 will help signer 2 (participant) to recover their share
151
+ helper1 = shares[0]
152
+ helper4 = shares[3]
153
+ helper5 = shares[4]
154
+ helper_shares = [helper1, helper4, helper5]
155
+ helpers = helper_shares.map(&:identifier)
156
+ participant_share = shares[1]
157
+
158
+ # Each helper computes delta values.
159
+ received_values = {}
160
+ helper_shares.each do |helper_share|
161
+ delta_values = FROST::Repairable.step1(helpers, participant_share.identifier, helper_share)
162
+ delta_values.each do |target_id, value|
163
+ received_values[target_id] ||= []
164
+ received_values[target_id] << value
165
+ end
166
+ end
167
+
168
+ # Each helper send sum value to participant.
169
+ participant_received_values = []
170
+ received_values.each do |_, values|
171
+ participant_received_values << FROST::Repairable.step2(values, ECDSA::Group::Secp256k1)
172
+ end
173
+
174
+ # Participant can obtain his share.
175
+ repair_share = FROST::Repairable.step3(2, participant_received_values, ECDSA::Group::Secp256k1)
176
+ ```
data/lib/frost/dkg.rb CHANGED
@@ -11,7 +11,7 @@ module FROST
11
11
  # @param [Integer] identifier
12
12
  # @param [ECDSA::Group] group Group of elliptic curve.
13
13
  # @return [Array] The triple of polynomial and public package(FROST::DKG::Package)
14
- def part1(identifier, min_signers, max_signers, group)
14
+ def generate_secret(identifier, min_signers, max_signers, group)
15
15
  raise ArgumentError, "identifier must be Integer" unless identifier.is_a?(Integer)
16
16
  raise ArgumentError, "identifier must be greater than 0." if identifier < 1
17
17
  raise ArgumentError, "group must be ECDSA::Group." unless group.is_a?(ECDSA::Group)
@@ -75,22 +75,34 @@ module FROST
75
75
  end
76
76
 
77
77
  # Generates the lagrange coefficient for the i'th participant.
78
+ # The Lagrange polynomial for a set of points (xj, yj) for 0 <= j <= k is
79
+ # ∑_{i=0}^k yi.ℓi(x), where ℓi(x) is the Lagrange basis polynomial:
80
+ # ℓi(x) = ∏_{0≤j≤k; j≠i} (x - xj) / (xi - xj).
81
+ # This computes ℓj(x) for the set of points `xs` and for the j corresponding to the given xj.
78
82
  # @param [Array] x_coordinates The list of x-coordinates.
79
83
  # @param [Integer] xi an x-coordinate contained in x_coordinates.
80
84
  # @param [ECDSA::Group] group Elliptic curve group.
85
+ # @param [Integer] x (Optional) if x is nil, it uses 0 for it (since Identifiers can't be 0).
81
86
  # @return [Integer] The lagrange coefficient.
82
- def self.derive_interpolating_value(x_coordinates, xi, group)
87
+ def self.derive_interpolating_value(x_coordinates, xi, group, x: nil)
83
88
  raise ArgumentError, "xi is not included in x_coordinates." unless x_coordinates.include?(xi)
84
89
  raise ArgumentError, "Duplicate values in x_coordinates." if (x_coordinates.length - x_coordinates.uniq.length) > 0
85
90
  raise ArgumentError, "group must be ECDSA::Group." unless group.is_a?(ECDSA::Group)
91
+ raise ArgumentError, "x must be Integer." if x && !x.is_a?(Integer)
86
92
 
87
93
  field = ECDSA::PrimeField.new(group.order)
88
94
  numerator = 1
89
95
  denominator = 1
96
+
90
97
  x_coordinates.each do |xj|
91
98
  next if xi == xj
92
- numerator *= xj
93
- denominator *= (xj - xi)
99
+ if x
100
+ numerator *= (x - xj)
101
+ denominator *= (xi - xj)
102
+ else
103
+ numerator *= xj
104
+ denominator *= (xj - xi)
105
+ end
94
106
  end
95
107
 
96
108
  field.mod(numerator * field.inverse(denominator))
@@ -0,0 +1,56 @@
1
+ module FROST
2
+ # Implements the Repairable Threshold Scheme (RTS) from <https://eprint.iacr.org/2017/1155>
3
+ module Repairable
4
+ module_function
5
+
6
+ # Step 1 for RTS.
7
+ # Each helper computes delta_i,j for other helpers.
8
+ # @param [Array] helpers Array of helper's identifier.
9
+ # @param [Integer] participant Identifier of the participant whose shares you want to restore.
10
+ # @param [FROST::SecretShare] share Share of participant running this process.
11
+ # @return [Hash] Hash with helper ID as key and value as delta value.
12
+ def step1(helpers, participant, share)
13
+ raise ArgumentError, "helpers must be greater than 1." if helpers.length < 2
14
+ raise ArgumentError, "participant must be greater than 1." if participant < 1
15
+ raise ArgumentError, "helpers has duplicate identifier." unless helpers.uniq.length == helpers.length
16
+ raise ArgumentError, "helpers contains same identifier with participant." if helpers.include?(participant)
17
+
18
+ field = ECDSA::PrimeField.new(share.group.order)
19
+ random_values = (helpers.length - 1).times.map { SecureRandom.random_number(share.group.order - 1) }
20
+
21
+ # compute last random value
22
+ ## Calculate Lagrange Coefficient for helper_i
23
+ zeta_i = Polynomial.derive_interpolating_value(helpers, share.identifier, share.group, x: participant)
24
+ lhs = field.mod(zeta_i * share.share)
25
+ # last random value
26
+ last = field.mod(lhs - random_values.sum)
27
+ random_values << last
28
+
29
+ helpers.zip(random_values).to_h
30
+ end
31
+
32
+ # Step 2 for RTS.
33
+ # Each helper sum received delta values from other helpers.
34
+ # @param [Array] step1_values Array of delta values.
35
+ # @param [ECDSA::Group] group
36
+ # @return [Integer] Sum of delta values.
37
+ def step2(step1_values, group)
38
+ raise ArgumentError, "group must be ECDSA::Group" unless group.is_a?(ECDSA::Group)
39
+
40
+ field = ECDSA::PrimeField.new(group.order)
41
+ field.mod(step1_values.sum)
42
+ end
43
+
44
+ # Participant compute own share with received sum of delta value.
45
+ # @param [Integer] identifier Identifier of the participant whose shares you want to restore.
46
+ # @param [Array] step2_results Array of Step 2 results received from other helpers.
47
+ # @param [ECDSA::Group] group
48
+ # @return
49
+ def step3(identifier, step2_results, group)
50
+ raise ArgumentError, "group must be ECDSA::Group" unless group.is_a?(ECDSA::Group)
51
+
52
+ field = ECDSA::PrimeField.new(group.order)
53
+ FROST::SecretShare.new(identifier, field.mod(step2_results.sum), group)
54
+ end
55
+ end
56
+ end
data/lib/frost/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FROST
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/frost.rb CHANGED
@@ -17,6 +17,7 @@ module FROST
17
17
  autoload :Polynomial, "frost/polynomial"
18
18
  autoload :SigningKey, "frost/signing_key"
19
19
  autoload :DKG, "frost/dkg"
20
+ autoload :Repairable, "frost/repairable"
20
21
 
21
22
  module_function
22
23
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: frostrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-16 00:00:00.000000000 Z
11
+ date: 2024-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa_ext
@@ -64,6 +64,7 @@ files:
64
64
  - lib/frost/hash.rb
65
65
  - lib/frost/nonce.rb
66
66
  - lib/frost/polynomial.rb
67
+ - lib/frost/repairable.rb
67
68
  - lib/frost/secret_share.rb
68
69
  - lib/frost/signature.rb
69
70
  - lib/frost/signing_key.rb