sharing 0.1.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2741454d22738f53a151a7720d1dec3efe64728e32b92efe103392c7ec46c2f
4
- data.tar.gz: '02118b02fe47723227a04e9203d28b248cdccc3b2ab9e747675b63f465856bf3'
3
+ metadata.gz: 719563755ad21ee302ca5c4cdf7dc99d5392f03f5a42af56a38a9199a76e5d31
4
+ data.tar.gz: f58c948f6c1c26cc2cc0f7f6e0c318a2c46d2c219952757bf43d092328f5b6ec
5
5
  SHA512:
6
- metadata.gz: e122938b94ee0077b3c90825df2afb2d3bbee50d474779876cf7ad0867f82b27f353303dfa9d6840b9aa6b15f4eb485cad154587811531add995fe5180ef3314
7
- data.tar.gz: 39487860406b1583b7d16beedb2dc0bec498058524972b16167d859d34a48f8e26f5d12218164ccd163690c678c1806615f90a9beb9e1b405c0f4e8e535c6629
6
+ metadata.gz: 678102cac63e5994565e6b7628e7ebd5865b696d996684a4ccfb1c073d8f028270989fc7f8771f0010df25529aaf97cb8265133b61e22b3188d11654c37ee851
7
+ data.tar.gz: a7903a1cdb18224fba5ceca2443c8856fca3368534c839bc5daf50a09dcbfdd8bb953c3d715534ec21d5f16b662d8f8d9e2a3162461b90a1763607c932447914
data/.codecov.yml ADDED
@@ -0,0 +1,6 @@
1
+ coverage:
2
+ status:
3
+ project:
4
+ default:
5
+ target: 99% # the required coverage value
6
+ threshold: 1% # the leniency in hitting the target
data/.rubocop.yml CHANGED
@@ -1,6 +1,9 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
3
  TargetRubyVersion: 2.6
4
+ Exclude:
5
+ - 'lab/**/*'
6
+ - 'vendor/**/*'
4
7
 
5
8
  Style/StringLiterals:
6
9
  Enabled: true
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
- ## [Unreleased]
1
+ ## [0.3.0] - 2022-05-23
2
2
 
3
- ## [0.1.0] - 2022-03-10
3
+ - Add division to Shamir's secret sharing implementation.
4
+
5
+ ## [0.2.0] - 2022-03-25
6
+
7
+ - Add multiplication to Shamir's secret sharing implementation and overall improvements.
8
+
9
+ ## [0.1.1] - 2022-03-25
10
+
11
+ - Fix threshold setting on Shamir's scheme v1
12
+
13
+ ## [0.1.0] - 2022-03-25
4
14
 
5
15
  - Initial release
data/Gemfile CHANGED
@@ -4,13 +4,9 @@ ruby ">= 2.6.0"
4
4
 
5
5
  source "https://rubygems.org"
6
6
 
7
- # Specify your gem's dependencies in secret_sharing.gemspec
7
+ # Specify your gem"s dependencies in sharing.gemspec
8
8
  gemspec
9
9
 
10
- gem "hensel_code", "~> 0.3.0"
11
-
12
- gem "prime", "~> 0.1.2"
13
-
14
10
  gem "rake", "~> 13.0"
15
11
 
16
12
  gem "minitest", "~> 5.0"
@@ -24,3 +20,5 @@ gem "rubocop-rake", "~> 0.6"
24
20
  gem "minitest-reporters", "~> 1.5"
25
21
 
26
22
  gem "codecov", require: false, group: :test
23
+
24
+ gem "simplecov-console", "~>0.9", require: false
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- secret_sharing (0.1.0)
5
- hensel_code (~> 0.3.0)
4
+ sharing (0.2.0)
5
+ hensel_code (~> 0.3.1)
6
6
  prime (~> 0.1.2)
7
7
 
8
8
  GEM
@@ -15,13 +15,16 @@ GEM
15
15
  simplecov (>= 0.15, < 0.22)
16
16
  docile (1.4.0)
17
17
  forwardable (1.3.2)
18
- hensel_code (0.3.0)
18
+ hensel_code (0.3.1)
19
+ openssl (~> 3.0.0)
20
+ prime (~> 0.1.2)
19
21
  minitest (5.15.0)
20
22
  minitest-reporters (1.5.0)
21
23
  ansi
22
24
  builder
23
25
  minitest (>= 5.0)
24
26
  ruby-progressbar
27
+ openssl (3.0.0)
25
28
  parallel (1.21.0)
26
29
  parser (3.1.1.0)
27
30
  ast (~> 2.4.1)
@@ -52,25 +55,31 @@ GEM
52
55
  docile (~> 1.1)
53
56
  simplecov-html (~> 0.11)
54
57
  simplecov_json_formatter (~> 0.1)
58
+ simplecov-console (0.9.1)
59
+ ansi
60
+ simplecov
61
+ terminal-table
55
62
  simplecov-html (0.12.3)
56
63
  simplecov_json_formatter (0.1.4)
57
64
  singleton (0.1.1)
65
+ terminal-table (3.0.2)
66
+ unicode-display_width (>= 1.1.1, < 3)
58
67
  unicode-display_width (2.1.0)
59
68
 
60
69
  PLATFORMS
61
70
  x86_64-darwin-19
71
+ x86_64-linux
62
72
 
63
73
  DEPENDENCIES
64
74
  codecov
65
- hensel_code (~> 0.3.0)
66
75
  minitest (~> 5.0)
67
76
  minitest-reporters (~> 1.5)
68
- prime (~> 0.1.2)
69
77
  rake (~> 13.0)
70
78
  rubocop (~> 1.21)
71
79
  rubocop-minitest (~> 0.17.2)
72
80
  rubocop-rake (~> 0.6)
73
- secret_sharing!
81
+ sharing!
82
+ simplecov-console (~> 0.9)
74
83
 
75
84
  RUBY VERSION
76
85
  ruby 3.1.0p0
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # Secret Sharing
1
+ # Sharing
2
2
 
3
- A secret sharing Ruby library.
3
+ ![GitHub Workflow Status](https://github.com/davidwilliam/sharing/actions/workflows/main.yml/badge.svg) [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop) ![GitHub](https://img.shields.io/github/license/davidwilliam/sharing) ![Gem](https://img.shields.io/gem/v/sharing) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/davidwilliam/sharing)
4
+
5
+ Sharing is a Ruby gem with implmementations of secret sharing schemes with homomorphic properties. Although secret sharing schemes and multiparty computation protocols are distinct notions, multiparty computation protocols are typically enabled by secret sharing schemes. In this setting, security comes from the use of multiple parties. If they collude, all security is lost, but satisfactory levels of security can be established by trusting a subset of them will not to collude. In many settings where corrupting security requires corrupting all the parties, and considering you are one of the computing parties, security is guaranteed if you are one of the parties.
6
+
7
+ Computing linear functions is trivial. Each non-linear operation however requires interaction between the parties/extra steps (for most secret sharing schemes).
4
8
 
5
9
  ## Installation
6
10
 
@@ -27,6 +31,13 @@ Secret Sharing currently supports two schemes:
27
31
 
28
32
  # Usage
29
33
 
34
+ In the examples below, there are two main levels of execution:
35
+
36
+ - Computations performed by the owner of the secrets (those are computations using instance methods)
37
+ - Computations performed over the secret shares (those are computations using class methods)
38
+
39
+ This distiction is important since we are showing everything at once here, for completeness and for clarity. However it is important to keep in mind that after the secret shares are generated, the computations over the shares are intended to be computed independetly by each participant (party), each one with their corresponding shares.
40
+
30
41
  ## Shamir's Secret Sharing V1
31
42
 
32
43
  The Shamir's secret sharing v1 scheme is based on the work of Adi Shamir in [How to Share a Secret](https://web.mit.edu/6.857/OldStuff/Fall03/ref/Shamir-HowToShareASecret.pdf).
@@ -58,9 +69,9 @@ We reconstruct the secrets as follows:
58
69
 
59
70
  ```ruby
60
71
  reconstructed_secret1 = sss.reconstruct_secret(shares1)
61
- # => (22/1)
72
+ # => 22
62
73
  reconstructed_secret2 = sss.reconstruct_secret(shares2)
63
- # => (36/1)
74
+ # => 36
64
75
  ```
65
76
 
66
77
  We can compute linear functions without requiring communication between the share holders:
@@ -80,18 +91,18 @@ and we can check that:
80
91
 
81
92
  ```ruby
82
93
  sss.reconstruct_secret(shares1_add_shares2)
83
- # => (58/1)
94
+ # => 58
84
95
  sss.reconstruct_secret(shares2_sub_shares1)
85
- # => (14/1)
96
+ # => 14
86
97
  sss.reconstruct_secret(shares1_smul_scalar)
87
- # => (44/1)
98
+ # => 44
88
99
  sss.reconstruct_secret(shares1_sdiv_scalar)
89
- # => (11/1)
100
+ # => 11
90
101
  ```
91
102
 
92
103
  ### Using Hensel Codes
93
104
 
94
- The gem Secret Sharing takes advantage of the gem [Hensel Codes](https://github.com/davidwilliam/hensel_code) for homomorphically encoding rational numbers as integers in order to compute over the integers and yet obtain results over the rationals.
105
+ The gem Secret Sharing takes advantage of the gem [Hensel Code](https://github.com/davidwilliam/hensel_code) for homomorphically encoding rational numbers as integers in order to compute over the integers and yet obtain results over the rationals.
95
106
 
96
107
  As most (if not all) of secret sharing schemes over finite fields `F_p` for `p > 2`, the secret inputs are naturally required to be positive integers in `F_p`. In this way, if we compute subtraction and we end up with a result that is negative, the reconstruction will fail (provided we don't have any econding in place). Same will occur if we compute a scalar division involving a scalar that is not a divisor of the corresponding secret. For addressing this and many other arithmetic problems, we can use Hensel codes to allow secret inputs to be positive and negative rational numbers.
97
108
 
@@ -143,13 +154,13 @@ We reconstruct the secrets:
143
154
 
144
155
  ```ruby
145
156
  reconstruct_secret1_add_secret2 = sss.reconstruct_secret(shares1_add_shares2)
146
- # => 3361138990/1
157
+ # => 3361138990
147
158
  reconstruct_secret1_sub_secret2 = sss.reconstruct_secret(shares1_sub_shares2)
148
- # => 2174854642/1
159
+ # => 2174854642
149
160
  reconstruct_shares1_smul_scalar = sss.reconstruct_secret(shares1_smul_scalar)
150
- # => 1383998411/1
161
+ # => 1383998411
151
162
  reconstruct_shares1_sdiv_scalar = sss.reconstruct_secret(shares1_sdiv_scalar)
152
- # => 3044796497/1
163
+ # => 3044796497
153
164
  ```
154
165
 
155
166
  and we can check that:
@@ -165,6 +176,178 @@ HenselCode::TruncatedFinitePadicExpansion.new(sss.p, 1, reconstructed_shares1_sd
165
176
  # => 2/15
166
177
  ```
167
178
 
179
+ ### Multiplication
180
+
181
+ As we previously saw, linear functions are easy to compute with shares created by an instance of Shamir's secret sharing scheme. Non-linear functions need some strategy that require extra steps in other to successfuly achieve the desired results. We implement multiplication in the context of Shamir's secret sharing scheme following the approach discussed by Dan Bognadov in [Foundations and properties of Shamir's secret sharing scheme - Research Seminar in Cryptography](https://uuslepo.it.da.ut.ee/~peeter_l/teaching/seminar07k/bogdanov.pdf).
182
+
183
+ We define an instance of Shamir's secret sharing scheme with the following parameters:
184
+
185
+ ```ruby
186
+ params = { lambda_: 16, total_shares: 6, threshold: 3 }
187
+ # => {:lambda_=>16, :total_shares=>6, :threshold=>3}
188
+ sss = Sharing::Polynomial::Shamir::V1.new params
189
+ # => #<Sharing::Polynomial::Shamir::V1:0x0000000105423640 @lambda_=16, @p=49367, @threshold=3, @total_shares=6>
190
+ ```
191
+
192
+ As before, we define the secrets and create shares for them:
193
+
194
+ ```ruby
195
+ secret1 = 13
196
+ secret2 = 28
197
+ shares1 = sss.create_shares(secret1)
198
+ # => [[1, 43064], [2, 20333], [3, 30554], [4, 24360], [5, 1751], [6, 12094]]
199
+ shares2 = sss.create_shares(secret2)
200
+ # => [[1, 7983], [2, 18517], [3, 31630], [4, 47322], [5, 16226], [6, 37076]]
201
+ ```
202
+
203
+ We combine both shares on a single array in preparation for the multiplication steps:
204
+
205
+ ```ruby
206
+ operands_shares = [shares1, shares2]
207
+ # => [[[1, 43064], [2, 20333], [3, 30554], [4, 24360], [5, 1751], [6, 12094]], [[1, 7983], [2, 18517], [3, 31630], [4, 47322], [5, 16226], [6, 37076]]]
208
+ ```
209
+
210
+ Recall we are working with a t-out-of-n secret sharing scheme and this is actually required in this setting. We have a total of `n = 6` shares and threshold `t = 3`. In order to correctly recover the result of the multiplication over shares, we need to select any combination of `2 * t - 1` shares out of the total number of shares:
211
+
212
+ ```ruby
213
+ selected_shares = Sharing::Polynomial::Shamir::V1.select_mul_shares(sss.total_shares, sss.threshold, operands_shares)
214
+ # => => [[[2, 20333], [1, 43064], [5, 1751], [3, 30554], [4, 24360]], [[2, 18517], [1, 7983], [5, 16226], [3, 31630], [4, 47322]]]
215
+ ```
216
+
217
+ Now we have everything we need to compute multiplication over the secret shares, which we do in two rounds. First round:
218
+
219
+ ```ruby
220
+ mul_round1 = Sharing::Polynomial::Shamir::V1.mul_first_round(selected_shares, sss.total_shares, sss.threshold, sss.lambda_, sss.p)
221
+ # => [[2, [[1, 25284], [2, 5881], [3, 2537], [4, 15252], [5, 44026], [6, 39492]]], [1, [[1, 36061], [2, 17299], [3, 32435], [4, 32102], [5, 16300], [6, 34396]]], [5, [[1, 30221], [2, 32724], [3, 33210], [4, 31679], [5, 28131], [6, 22566]]], [3, [[1, 46172], [2, 33017], [3, 8081], [4, 20731], [5, 21600], [6, 10688]]], [4, [[1, 12410], [2, 39133], [3, 5920], [4, 11505], [5, 6521], [6, 40335]]]]
222
+ ```
223
+
224
+ Then we perform the second round:
225
+
226
+ ```ruby
227
+ mul_round2 = Sharing::Polynomial::Shamir::V1.mul_second_round(mul_round1)
228
+ # => [[1, 150148], [2, 128054], [3, 82183], [4, 111269], [5, 116578], [6, 147477]]
229
+ ```
230
+
231
+ Then we only need a number equal to the threshold to reconstruct the result of the multipliction over the shares:
232
+
233
+ ```ruby
234
+ selected_multiplication_shares = mul_round2.sample(sss.threshold)
235
+ # => [[6, 147477], [2, 128054], [1, 150148]
236
+ sss.reconstruct_secret(selected_multiplication_shares)
237
+ # => 364
238
+ ```
239
+
240
+ and we can check that
241
+
242
+ ```ruby
243
+ secret1 * secret2
244
+ # => 364
245
+ ```
246
+
247
+ ### t-out-of-n Secret Sharing
248
+
249
+ Now we defined a threshold value that is less than the total number of shares:
250
+
251
+ ```ruby
252
+ params = {total_shares: 5, threshold: 3, lambda_: 16}
253
+ # => {:total_shares=>5, :threshold=>3, :lambda_=>16}
254
+ sss = Sharing::Polynomial::Shamir::V1.new params
255
+ # => #<Sharing::Polynomial::Shamir::V1:0x000000010b046e90 @lambda_=16, @p=61343, @threshold=3, @total_shares=5>
256
+ secret = 25
257
+ # => 25
258
+ shares = sss.create_shares(secret)
259
+ # => [[1, 54707], [2, 50401], [3, 48450], [4, 48854], [5, 51613]]
260
+ selected_shares = shares.sample(3)
261
+ reconstructed_secret = sss.reconstruct_secret(selected_shares)
262
+ # => 25
263
+ ```
264
+
265
+ Everything else works the sabe as before except the fact that only `3` shares are required to reconstruct the secret.
266
+
267
+ ### Division
268
+
269
+ Here we simulate a division algorithm computed over the shares. Consider the following configuration:
270
+
271
+ ```ruby
272
+ {:lambda_=>16, :total_shares=>6, :threshold=>3}
273
+ sss = Sharing::Polynomial::Shamir::V1.new(params)
274
+ ```
275
+
276
+ and the rational numbers
277
+
278
+ ```ruby
279
+ rat1 = Rational(2, 3)
280
+ rat2 = Rational(-5, 7)
281
+ ```
282
+
283
+ We compute their Hensel code so we define our secrets:
284
+
285
+ ```ruby
286
+ secret1 = HenselCode::TFPE.new(sss.p, 1, rat1).hensel_code
287
+ # => 40896
288
+ secret2 = HenselCode::TFPE.new(sss.p, 1, rat2).hensel_code
289
+ # => 52579
290
+ ```
291
+
292
+ We then comute the shares for each secret:
293
+
294
+ ```ruby
295
+ shares1 = sss.create_shares(secret1)
296
+ # => [[1, 22792], [2, 38518], [3, 26731], [4, 48774], [5, 43304], [6, 10321]]
297
+ shares2 = sss.create_shares(secret2)
298
+ # => [[1, 37982], [2, 294], [3, 858], [4, 39674], [5, 55399], [6, 48033]]
299
+ ```
300
+
301
+ We select a subset of the shares of eacg secret based on the threshold:
302
+
303
+ ```ruby
304
+ selected_shares1 = shares1.sample(sss.threshold)
305
+ # => [[1, 22792], [5, 43304], [6, 10321]]
306
+ selected_shares2 = shares2.sample(sss.threshold)
307
+ # => [[6, 48033], [2, 294], [1, 37982]]
308
+ ```
309
+
310
+ A party that is not one of the selected parties (a trusted party for instance) generates three random values that will later used:
311
+
312
+ ```ruby
313
+ r1, r2, r3 = Sharing::Polynomial::Shamir::V1.generate_division_masking(sss.p)
314
+ => [23078, 18925, 18812]
315
+ ```
316
+
317
+ Each party multiply their shares of the first operand by `r1` and the shares of the second operand by `r2`. For convenience (given our simulation), this step is here done all at once as follows:
318
+
319
+ ```ruby
320
+ cs, ds = Sharing::Polynomial::Shamir::V1.compute_numerator_denominator(selected_shares1, selected_shares2, r1, r2, sss.p)
321
+ => [[[1, 38894], [5, 30899], [6, 54512]], [[6, 43951], [2, 43080], [1, 53419]]]
322
+ ```
323
+
324
+ `cs` denote the shares representing the numerator of the division and `ds` represent the shares of the denominator of the division, as in `c/d`.
325
+
326
+ Finally, in the reconstruction step, `c` and `d` are reconstructed and `r3` is used to invert the multiplication by `r1` and `r2` that was previously computed by the parties. With the correct values recovered, we compute the Hensel decoding and then we obtain the final result without revealing what the numerator and denominator were. For convenience, we execute these steps as follows:
327
+
328
+ ```ruby
329
+ sss.reconstruct_division(cs, ds, r3)
330
+ # => (-14/15)
331
+ ```
332
+
333
+ We can also use only a subset of the shares:
334
+
335
+ ```ruby
336
+ selected_cs = cs.sample(sss.threshold)
337
+ => [[5, 30899], [1, 38894], [6, 54512]]
338
+ selected_ds = ds.sample(sss.threshold)
339
+ # => [[2, 43080], [1, 53419], [6, 43951]]
340
+ sss.reconstruct_division(selected_cs, selected_ds, r3)
341
+ => (-14/15)
342
+ ```
343
+
344
+ And we can check that
345
+
346
+ ```ruby
347
+ rat1 / rat2
348
+ # => (-14/15)
349
+ ```
350
+
168
351
  ## Asmuth-Bloom V2
169
352
 
170
353
  The Asmuth-Bloom V2 was proposed by Ersoy et al. in in [Homomorphic extensions of CRT-based secret sharing](https://www.sciencedirect.com/science/article/pii/S0166218X20303012)). The reference is a CRT-based secret sharing scheme introduced by Asmuth-Bloom in [A modular approach to key safeguarding](https://ieeexplore.ieee.org/abstract/document/1056651).
@@ -227,6 +410,19 @@ crtss.reconstruct_secret(selected_shares1_add_shares2_add_shares1_mul_shares2)
227
410
 
228
411
  and we can check that 5 + 8 = 13, 5 * 8 = 40, and 13 + 40 = 53.
229
412
 
413
+ ## Author
414
+
415
+ David William Silva
416
+
417
+ ## Contributors
418
+
419
+ David William Silva (Algemetric)
420
+ Marcio Junior (Algemetric)
421
+
422
+ ## Acknowledgements
423
+
424
+ Luke Harmon (Algemetric) and Gaetan Delavignette (Algemetric) have been instrumental by providing/conducting mathematical analyses, tests, and overall recommendations for improving the gem.
425
+
230
426
  ## Development
231
427
 
232
428
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/console CHANGED
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "bundler/setup"
5
- require "secret_sharing"
5
+ require "sharing"
6
6
 
7
7
  # You can add fixtures and/or initialization code here to make experimenting
8
8
  # with your gem easier. You can also use a different console, if you like.
data/codecov ADDED
Binary file
@@ -28,15 +28,59 @@ module Sharing
28
28
  shares.map { |s| [s[0], (s[1] * mod_inverse(scalar, prime)) % prime] }
29
29
  end
30
30
 
31
+ def self.mul_first_round(shares, total_shares, threshold, lambda_, prime)
32
+ shares1, shares2 = shares
33
+ xs = shares1.map(&:first)
34
+ shares1.zip(shares2).map.with_index do |s, i|
35
+ share = prepare_share_for_multiplication(i, xs, prime, s)
36
+ reshares = create_shares(share, total_shares, threshold, lambda_, prime)
37
+ encode_reshares(reshares, prime, s)
38
+ end
39
+ end
40
+
41
+ def self.encode_reshares(reshares, prime, s_pair)
42
+ reshares_encoded = reshares.map do |ss|
43
+ [ss[0], (ss[1].numerator * mod_inverse(ss[1].denominator, prime)) % prime]
44
+ end
45
+ [s_pair[0][0], reshares_encoded]
46
+ end
47
+
48
+ def self.prepare_share_for_multiplication(index, xs_, prime, s_pair)
49
+ beta = lagrange_basis_polynomial_inner_loop(index, xs_)
50
+ (s_pair[0][1] * s_pair[1][1] * beta) % prime
51
+ end
52
+
53
+ def self.mul_second_round(mul_round1)
54
+ multiplication_shares = mul_round1.map(&:last).map { |m| m.map(&:last) }.transpose.map(&:sum)
55
+ multiplication_shares.map.with_index { |m, i| [i + 1, m] }
56
+ end
57
+
58
+ def self.select_mul_shares(total_shares, threshold, shares)
59
+ indices = (0..total_shares - 1).to_a.sample((2 * threshold) - 1)
60
+ shares.map { |shares_| shares_.values_at(*indices) }
61
+ end
62
+
31
63
  def self.generate_random_coefficients(total_shares, lambda_)
32
64
  random_distinct_numbers("integer", total_shares - 1, lambda_ - 1)
33
65
  end
34
66
 
35
- def self.create_shares(secret, total_shares, lambda_, prime)
36
- random_coefficients = generate_random_coefficients(total_shares, lambda_)
67
+ def self.create_shares(secret, total_shares, threshold, lambda_, prime)
68
+ random_coefficients = generate_random_coefficients(threshold, lambda_)
37
69
  (1..total_shares).map.with_index { |x, i| [i + 1, f(x, secret, random_coefficients) % prime] }
38
70
  end
39
71
 
72
+ def self.generate_division_masking(prime)
73
+ r1, r2 = random_distinct_numbers("integer", 2, prime.bit_length - 1)
74
+ r3 = (r2 * mod_inverse(r1, prime)) % prime
75
+ [r1, r2, r3]
76
+ end
77
+
78
+ def self.compute_numerator_denominator(shares1, shares2, r1_, r2_, prime)
79
+ cs = shares1.map { |i, share| [i, (share * r1_) % prime] }
80
+ ds = shares2.map { |i, share| [i, (share * r2_) % prime] }
81
+ [cs, ds]
82
+ end
83
+
40
84
  def initialize(params = {})
41
85
  @lambda_ = params[:lambda_]
42
86
  @total_shares = params[:total_shares]
@@ -44,16 +88,27 @@ module Sharing
44
88
  generate_prime
45
89
  end
46
90
 
91
+ def params
92
+ [lambda_, p, total_shares, threshold]
93
+ end
94
+
47
95
  def create_shares(secret)
48
96
  random_coefficients = generate_random_coefficients
49
- (1..total_shares).map.with_index { |x, i| [i + 1, f(x, secret, random_coefficients) % p] }
97
+ (1..total_shares).map { |x| [x, f(x, secret, random_coefficients) % p] }
50
98
  end
51
99
 
52
100
  def reconstruct_secret(points)
53
101
  xs = points.map(&:first)
54
102
  ys = points.map(&:last)
55
103
  l0s = lagrange_basis_polynomial(xs)
56
- l0s.zip(ys).map { |l, y| l * y }.sum % p
104
+ reconstructed_secret = l0s.zip(ys).map { |l, y| l * y }.sum % p
105
+ encode_to_integer(reconstructed_secret)
106
+ end
107
+
108
+ def reconstruct_division(cs_, ds_, r3_)
109
+ c, d = [cs_, ds_].map { |shares| reconstruct_secret(shares) }
110
+ c_d_encoded = (c * mod_inverse(d, p) * r3_) % p
111
+ HenselCode::TruncatedFinitePadicExpansion.new(p, 1, c_d_encoded).to_r
57
112
  end
58
113
 
59
114
  private
@@ -63,7 +118,11 @@ module Sharing
63
118
  end
64
119
 
65
120
  def generate_random_coefficients
66
- random_distinct_numbers("integer", total_shares - 1, lambda_ - 1)
121
+ random_distinct_numbers("integer", threshold - 1, lambda_ - 1)
122
+ end
123
+
124
+ def encode_to_integer(reconstructed_secret)
125
+ (reconstructed_secret.numerator * mod_inverse(reconstructed_secret.denominator, p)) % p
67
126
  end
68
127
  end
69
128
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sharing
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/sharing.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ["lib"]
31
31
 
32
32
  # Uncomment to register a new dependency of your gem
33
- spec.add_dependency "hensel_code", "~> 0.3.0"
33
+ spec.add_dependency "hensel_code", "~> 0.3.1"
34
34
  spec.add_dependency "prime", "~> 0.1.2"
35
35
 
36
36
  # For more information and examples about making a new gem, check out our
@@ -1,4 +1,4 @@
1
- module SecretSharing
1
+ module Sharing
2
2
  VERSION: String
3
3
  # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sharing
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David William Silva
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-25 00:00:00.000000000 Z
11
+ date: 2022-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hensel_code
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.3.0
19
+ version: 0.3.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.3.0
26
+ version: 0.3.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: prime
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -45,6 +45,7 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - ".codecov.yml"
48
49
  - ".rubocop.yml"
49
50
  - CHANGELOG.md
50
51
  - Gemfile
@@ -54,6 +55,7 @@ files:
54
55
  - Rakefile
55
56
  - bin/console
56
57
  - bin/setup
58
+ - codecov
57
59
  - lib/sharing.rb
58
60
  - lib/sharing/crt/asmuth-bloom/v2.rb
59
61
  - lib/sharing/polynomial/shamir/v1.rb