sharing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a2741454d22738f53a151a7720d1dec3efe64728e32b92efe103392c7ec46c2f
4
+ data.tar.gz: '02118b02fe47723227a04e9203d28b248cdccc3b2ab9e747675b63f465856bf3'
5
+ SHA512:
6
+ metadata.gz: e122938b94ee0077b3c90825df2afb2d3bbee50d474779876cf7ad0867f82b27f353303dfa9d6840b9aa6b15f4eb485cad154587811531add995fe5180ef3314
7
+ data.tar.gz: 39487860406b1583b7d16beedb2dc0bec498058524972b16167d859d34a48f8e26f5d12218164ccd163690c678c1806615f90a9beb9e1b405c0f4e8e535c6629
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.6
4
+
5
+ Style/StringLiterals:
6
+ Enabled: true
7
+ EnforcedStyle: double_quotes
8
+
9
+ Style/StringLiteralsInInterpolation:
10
+ Enabled: true
11
+ EnforcedStyle: double_quotes
12
+
13
+ Layout/LineLength:
14
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-03-10
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ ruby ">= 2.6.0"
4
+
5
+ source "https://rubygems.org"
6
+
7
+ # Specify your gem's dependencies in secret_sharing.gemspec
8
+ gemspec
9
+
10
+ gem "hensel_code", "~> 0.3.0"
11
+
12
+ gem "prime", "~> 0.1.2"
13
+
14
+ gem "rake", "~> 13.0"
15
+
16
+ gem "minitest", "~> 5.0"
17
+
18
+ gem "rubocop", "~> 1.21"
19
+
20
+ gem "rubocop-minitest", "~> 0.17.2"
21
+
22
+ gem "rubocop-rake", "~> 0.6"
23
+
24
+ gem "minitest-reporters", "~> 1.5"
25
+
26
+ gem "codecov", require: false, group: :test
data/Gemfile.lock ADDED
@@ -0,0 +1,79 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ secret_sharing (0.1.0)
5
+ hensel_code (~> 0.3.0)
6
+ prime (~> 0.1.2)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ ansi (1.5.0)
12
+ ast (2.4.2)
13
+ builder (3.2.4)
14
+ codecov (0.6.0)
15
+ simplecov (>= 0.15, < 0.22)
16
+ docile (1.4.0)
17
+ forwardable (1.3.2)
18
+ hensel_code (0.3.0)
19
+ minitest (5.15.0)
20
+ minitest-reporters (1.5.0)
21
+ ansi
22
+ builder
23
+ minitest (>= 5.0)
24
+ ruby-progressbar
25
+ parallel (1.21.0)
26
+ parser (3.1.1.0)
27
+ ast (~> 2.4.1)
28
+ prime (0.1.2)
29
+ forwardable
30
+ singleton
31
+ rainbow (3.1.1)
32
+ rake (13.0.6)
33
+ regexp_parser (2.2.1)
34
+ rexml (3.2.5)
35
+ rubocop (1.26.0)
36
+ parallel (~> 1.10)
37
+ parser (>= 3.1.0.0)
38
+ rainbow (>= 2.2.2, < 4.0)
39
+ regexp_parser (>= 1.8, < 3.0)
40
+ rexml
41
+ rubocop-ast (>= 1.16.0, < 2.0)
42
+ ruby-progressbar (~> 1.7)
43
+ unicode-display_width (>= 1.4.0, < 3.0)
44
+ rubocop-ast (1.16.0)
45
+ parser (>= 3.1.1.0)
46
+ rubocop-minitest (0.17.2)
47
+ rubocop (>= 0.90, < 2.0)
48
+ rubocop-rake (0.6.0)
49
+ rubocop (~> 1.0)
50
+ ruby-progressbar (1.11.0)
51
+ simplecov (0.21.2)
52
+ docile (~> 1.1)
53
+ simplecov-html (~> 0.11)
54
+ simplecov_json_formatter (~> 0.1)
55
+ simplecov-html (0.12.3)
56
+ simplecov_json_formatter (0.1.4)
57
+ singleton (0.1.1)
58
+ unicode-display_width (2.1.0)
59
+
60
+ PLATFORMS
61
+ x86_64-darwin-19
62
+
63
+ DEPENDENCIES
64
+ codecov
65
+ hensel_code (~> 0.3.0)
66
+ minitest (~> 5.0)
67
+ minitest-reporters (~> 1.5)
68
+ prime (~> 0.1.2)
69
+ rake (~> 13.0)
70
+ rubocop (~> 1.21)
71
+ rubocop-minitest (~> 0.17.2)
72
+ rubocop-rake (~> 0.6)
73
+ secret_sharing!
74
+
75
+ RUBY VERSION
76
+ ruby 3.1.0p0
77
+
78
+ BUNDLED WITH
79
+ 2.3.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 David William Silva
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # Secret Sharing
2
+
3
+ A secret sharing Ruby library.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'sharing'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install sharing
20
+
21
+ # Supported Secret Sharing Schemes
22
+
23
+ Secret Sharing currently supports two schemes:
24
+
25
+ - A first version of the Shamir's secret sharing
26
+ - The second of two modified versions of the CRT-based Asmuth-Bloom scheme proposed by Ersoy et al.
27
+
28
+ # Usage
29
+
30
+ ## Shamir's Secret Sharing V1
31
+
32
+ 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).
33
+
34
+ ### n-out-of-n Shamir Secret Sharing
35
+
36
+ Let's consider the followin setup:
37
+
38
+ ```ruby
39
+ secret1 = 22
40
+ secret2 = 36
41
+ scalar = 2
42
+ params = {total_shares: 5, threshold: 5, lambda_: 16}
43
+ sss = Sharing::Polynomial::Shamir::V1.new params
44
+ # => #<Sharing::Polynomial::Shamir::V1:0x0000000114090618 @lambda_=16, @p=63719, @threshold=3, @total_shares=5>
45
+ ```
46
+
47
+ We generate shares as follows:
48
+
49
+ ```ruby
50
+ shares1 = sss.create_shares(secret1)
51
+ # => [[1, 17038], [2, 51463], [3, 24539], [4, 33770], [5, 34327]]
52
+ shares2 = sss.create_shares(secret2)
53
+ # => [[1, 26584], [2, 37554], [3, 53948], [4, 45589], [5, 58559]]
54
+ scalar = 2
55
+ ```
56
+
57
+ We reconstruct the secrets as follows:
58
+
59
+ ```ruby
60
+ reconstructed_secret1 = sss.reconstruct_secret(shares1)
61
+ # => (22/1)
62
+ reconstructed_secret2 = sss.reconstruct_secret(shares2)
63
+ # => (36/1)
64
+ ```
65
+
66
+ We can compute linear functions without requiring communication between the share holders:
67
+
68
+ ```ruby
69
+ shares1_add_shares2 = Sharing::Polynomial::Shamir::V1.add(shares1, shares2, sss.p)
70
+ # => [[1, 43622], [2, 25298], [3, 14768], [4, 15640], [5, 29167]]
71
+ shares2_sub_shares1 = Sharing::Polynomial::Shamir::V1.sub(shares2, shares1, sss.p)
72
+ # => [[1, 9546], [2, 49810], [3, 29409], [4, 11819], [5, 24232]]
73
+ shares1_smul_scalar = Sharing::Polynomial::Shamir::V1.smul(shares1, scalar, sss.p)
74
+ # => [[1, 34076], [2, 39207], [3, 49078], [4, 3821], [5, 4935]]
75
+ shares1_sdiv_scalar = Sharing::Polynomial::Shamir::V1.sdiv(shares1, scalar, sss.p)
76
+ # => [[1, 8519], [2, 57591], [3, 44129], [4, 16885], [5, 49023]]
77
+ ```
78
+
79
+ and we can check that:
80
+
81
+ ```ruby
82
+ sss.reconstruct_secret(shares1_add_shares2)
83
+ # => (58/1)
84
+ sss.reconstruct_secret(shares2_sub_shares1)
85
+ # => (14/1)
86
+ sss.reconstruct_secret(shares1_smul_scalar)
87
+ # => (44/1)
88
+ sss.reconstruct_secret(shares1_sdiv_scalar)
89
+ # => (11/1)
90
+ ```
91
+
92
+ ### Using Hensel Codes
93
+
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.
95
+
96
+ 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
+
98
+ ```ruby
99
+ rational_secret1 = Rational(2,3)
100
+ # => 2/3
101
+ rational_secret2 = Rational(-5,7)
102
+ # => -5/7
103
+ scalar = 5
104
+ # => 5
105
+ params = {total_shares: 5, threshold: 5, lambda_: 32}
106
+ # => {:total_shares=>5, :threshold=>5, :lambda_=>32}
107
+ sss = Sharing::Polynomial::Shamir::V1.new params
108
+ # => #<Sharing::Polynomial::Shamir::V1:0x0000000103065cd0 @lambda_=32, @total_shares=5, @threshold=5, @p=4151995223>
109
+ ```
110
+
111
+ We compute the Hensel codes for the secrets:
112
+
113
+ ```ruby
114
+ secret1 = HenselCode::TruncatedFinitePadicExpansion.new(sss.p, 1, rational_secret1).hensel_code
115
+ # => 2767996816
116
+ secret2 = HenselCode::TruncatedFinitePadicExpansion.new(sss.p, 1, rational_secret2).hensel_code
117
+ # => 593142174
118
+ ```
119
+
120
+ Then, we create the shares:
121
+
122
+ ```ruby
123
+ shares1 = sss.create_shares(secret1)
124
+ # => [[1, 1788895381], [2, 1795799163], [3, 3852643947], [4, 58410522], [5, 2611091242]]
125
+ shares2 = sss.create_shares(secret2)
126
+ # => [[1, 2523224758], [2, 2966680092], [3, 3722500411], [4, 3217222534], [5, 656923087]]
127
+ ```
128
+
129
+ Now we can compute all the available linear computations as before:
130
+
131
+ ```ruby
132
+ shares1_add_shares2 = Sharing::Polynomial::Shamir::V1.add(shares1, shares2, sss.p)
133
+ # => [[1, 160124916], [2, 610484032], [3, 3423149135], [4, 3275633056], [5, 3268014329]]
134
+ shares1_sub_shares2 = Sharing::Polynomial::Shamir::V1.sub(shares1, shares2, sss.p)
135
+ # => [[1, 3417665846], [2, 2981114294], [3, 130143536], [4, 993183211], [5, 1954168155]]
136
+ shares1_smul_scalar = Sharing::Polynomial::Shamir::V1.smul(shares1, scalar, sss.p)
137
+ # => [[1, 640486459], [2, 675005369], [3, 2655238843], [4, 292052610], [5, 599470541]]
138
+ shares1_sdiv_scalar = Sharing::Polynomial::Shamir::V1.sdiv(shares1, scalar, sss.p)
139
+ # => [[1, 2848976210], [2, 3680756011], [3, 1600927834], [4, 842081149], [5, 1352617293]]
140
+ ```
141
+
142
+ We reconstruct the secrets:
143
+
144
+ ```ruby
145
+ reconstruct_secret1_add_secret2 = sss.reconstruct_secret(shares1_add_shares2)
146
+ # => 3361138990/1
147
+ reconstruct_secret1_sub_secret2 = sss.reconstruct_secret(shares1_sub_shares2)
148
+ # => 2174854642/1
149
+ reconstruct_shares1_smul_scalar = sss.reconstruct_secret(shares1_smul_scalar)
150
+ # => 1383998411/1
151
+ reconstruct_shares1_sdiv_scalar = sss.reconstruct_secret(shares1_sdiv_scalar)
152
+ # => 3044796497/1
153
+ ```
154
+
155
+ and we can check that:
156
+
157
+ ```ruby
158
+ HenselCode::TruncatedFinitePadicExpansion.new(sss.p, 1, reconstructed_secret1_add_secret2).to_r
159
+ # => -1/21
160
+ HenselCode::TruncatedFinitePadicExpansion.new(sss.p, 1, reconstructed_secret1_sub_secret2).to_r
161
+ # => 29/21
162
+ HenselCode::TruncatedFinitePadicExpansion.new(sss.p, 1, reconstructed_shares1_smul_scalar).to_r
163
+ # => 10/3
164
+ HenselCode::TruncatedFinitePadicExpansion.new(sss.p, 1, reconstructed_shares1_sdiv_scalar).to_r
165
+ # => 2/15
166
+ ```
167
+
168
+ ## Asmuth-Bloom V2
169
+
170
+ 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).
171
+
172
+ We have currently the class `Sharing::CRT::AsmuthBloom::V2`. To initialize it, we need to pass the following parameters:
173
+
174
+ - `lambda_`: the bit length of the secret prime moduli.
175
+ - `threshold`: the recovery threshold in which it is guaranteed to recover the secret for all possible values.
176
+ - `secrecy`: the secrecy threshold in which it is guaranteed that no information is revealed about the secret.
177
+ - `total_shares`: the total number of shares we want to create for any given secret.
178
+ - `k_add`: the provisioned number of additions we want to compute over shares.
179
+ - `k_add`: the provisioned number of multiplicaitons we want to compute over shares.
180
+
181
+ ```ruby
182
+ params = { lambda_: 64, threshold: 10, secrecy: 3, total_shares: 13, k_add: 5000, k_mul: 2 }
183
+ crtss = Sharing::CRTAsmuthBloomV2.new params
184
+ secret1 = 5
185
+ secret2 = 8
186
+ secret3 = 9
187
+ shares1 = crtss.compute_shares(secret1)
188
+ # => [[0, 4185685952388161215], [1, 7431082072249155627], [2, 3172867207673420707], [3, 14094855932661978905], [4, 8449128283552032507], [5, 7274923078167548868], [6, 3443672123003372167], [7, 3449028625755130838], [8, 5794221801968596287], [9, 3328886357835317095], [10, 7488573194762652917], [11, 8211719263601562780], [12, 12709118192143848454]]
189
+ shares2 = crtss.compute_shares(secret2)
190
+ # => [[0, 2233143134937563130], [1, 10799196764783177850], [2, 3895399176949806798], [3, 1198864688298180029], [4, 10749884548017217271], [5, 8208674321670086887], [6, 2822739185939232463], [7, 6792525158886356123], [8, 11182441441011760155], [9, 5065252015479538675], [10, 14231070486785344674], [11, 12955329422395114581], [12, 13444354079541844356]]
191
+ shares3 = crtss.compute_shares(secret3)
192
+ # => [[0, 8784211624348791413], [1, 3173871378881529520], [2, 13248531955944997083], [3, 1782634630778360250], [4, 13054421101338573568], [5, 12464404777826322232], [6, 10309434923908541341], [7, 11837628883332260554], [8, 7022273320911219172], [9, 2554741791512322214], [10, 11331979459726025879], [11, 5610455238685743922], [12, 3003841353993251584]]
193
+ ```
194
+
195
+ With the shares created, we can compute basic arithmetic:
196
+
197
+ ```ruby
198
+ shares1_add_shares2 = Sharing::CRTAsmuthBloomV2.add(shares1, shares2)
199
+ # => [[0, 6418829087325724345], [1, 18230278837032333477], [2, 7068266384623227505], [3, 15293720620960158934], [4, 19199012831569249778], [5, 15483597399837635755], [6, 6266411308942604630], [7, 10241553784641486961], [8, 16976663242980356442], [9, 8394138373314855770], [10, 21719643681547997591], [11, 21167048685996677361], [12, 26153472271685692810]]
200
+ shares1_mul_shares2 = Sharing::CRTAsmuthBloomV2.mul(shares1, shares2)
201
+ # => [[0, 9347235849580217942880423213680002950], [1, 80249717473471354529348429396269261950], [2, 12359584309342074742148630183422566186], [3, 16897825064318556900157377557090288245], [4, 90827153579571227732364682022273828397], [5, 59717474263879064686877969113378493916], [6, 9720588245128167152782160092717057321], [7, 23427613714160960605536958120927421074], [8, 64793545996747467446843059564467544485], [9, 16861648333327680706874620252941149125], [10, 106570412980118630776546521824476514058], [11, 106385528184186069985041673188764895180], [12, 170865885013928618679442811690919225624]]
202
+ shares1_add_shares2_add_shares1_mul_shares2 = Sharing::CRTAsmuthBloomV2.add(shares1_add_shares2, shares1_mul_shares2)
203
+ # => [[0, 9347235849580217949299252301005727295], [1, 80249717473471354547578708233301595427], [2, 12359584309342074749216896568045793691], [3, 16897825064318556915451098178050447179], [4, 90827153579571227751563694853843078175], [5, 59717474263879064702361566513216129671], [6, 9720588245128167159048571401659661951], [7, 23427613714160960615778511905568908035], [8, 64793545996747467463819722807447900927], [9, 16861648333327680715268758626256004895], [10, 106570412980118630798266165506024511649], [11, 106385528184186070006208721874761572541], [12, 170865885013928618705596283962604918434]]
204
+ ```
205
+
206
+ In order to recover the associated secrets with the shares we just generated via basic arithmetic computations, we select a number of shares for reconstruction (in any random order). It could be the total number of shares or a smaller number. We will choose the exact number of the recovery threshold:
207
+
208
+ ```ruby
209
+ selected_shares1_add_shares2 = shares1_add_shares2.sample(params[:threshold])
210
+ # => [[10, 21719643681547997591], [0, 6418829087325724345], [9, 8394138373314855770], [6, 6266411308942604630], [1, 18230278837032333477], [7, 10241553784641486961], [11, 21167048685996677361], [5, 15483597399837635755], [8, 16976663242980356442], [12, 26153472271685692810]]
211
+ selected_shares1_mul_shares2 = shares1_mul_shares2.sample(params[:threshold])
212
+ # => [[5, 59717474263879064686877969113378493916], [1, 80249717473471354529348429396269261950], [12, 170865885013928618679442811690919225624], [7, 23427613714160960605536958120927421074], [6, 9720588245128167152782160092717057321], [0, 9347235849580217942880423213680002950], [3, 16897825064318556900157377557090288245], [9, 16861648333327680706874620252941149125], [2, 12359584309342074742148630183422566186], [4, 90827153579571227732364682022273828397]]
213
+ selected_shares1_add_shares2_add_shares1_mul_shares2 = shares1_add_shares2_add_shares1_mul_shares2.sample(params[:threshold])
214
+ # => [[11, 106385528184186070006208721874761572541], [6, 9720588245128167159048571401659661951], [2, 12359584309342074749216896568045793691], [4, 90827153579571227751563694853843078175], [12, 170865885013928618705596283962604918434], [7, 23427613714160960615778511905568908035], [9, 16861648333327680715268758626256004895], [5, 59717474263879064702361566513216129671], [10, 106570412980118630798266165506024511649], [0, 9347235849580217949299252301005727295]]
215
+ ```
216
+
217
+ Finally, we reconstruct the secrets:
218
+
219
+ ```ruby
220
+ crtss.reconstruct_secret(selected_shares1_add_shares2)
221
+ # => 13
222
+ crtss.reconstruct_secret(selected_shares1_mul_shares2)
223
+ # => 40
224
+ crtss.reconstruct_secret(selected_shares1_add_shares2_add_shares1_mul_shares2)
225
+ # => 53
226
+ ```
227
+
228
+ and we can check that 5 + 8 = 13, 5 * 8 = 40, and 13 + 40 = 53.
229
+
230
+ ## Development
231
+
232
+ 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.
233
+
234
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
235
+
236
+ ## Contributing
237
+
238
+ Bug reports and pull requests are welcome on GitHub at https://github.com/davidwilliam/sharing.
239
+
240
+ ## License
241
+
242
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ task default: %i[test rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "secret_sharing"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # module secret sharing
4
+ module Sharing
5
+ # crt-based secret sharing schemes
6
+ module CRT
7
+ # crt-based secret sharing scheme proposed by Asmuth-Bloom
8
+ module AsmuthBloom
9
+ # second version of Asmuth-Bloom proposed by Ersoy et al.
10
+ class V2
11
+ include HenselCode::Tools
12
+
13
+ attr_accessor :threshold, :secrecy, :total_shares, :primes,
14
+ :p, :m_r, :m_to_s, :k_add, :k_mul, :lambda_,
15
+ :a, :y,
16
+ :upperbound
17
+
18
+ def self.add(shares1, shares2)
19
+ shares1.zip(shares2).map { |s| [s[0][0], s[0][1] + s[1][1]] }
20
+ end
21
+
22
+ def self.mul(shares1, shares2)
23
+ shares1.zip(shares2).map { |s| [s[0][0], s[0][1] * s[1][1]] }
24
+ end
25
+
26
+ def initialize(params = {})
27
+ @threshold = params[:threshold]
28
+ @secrecy = params[:secrecy]
29
+ @total_shares = params[:total_shares]
30
+ @k_add = params[:k_add]
31
+ @k_mul = params[:k_mul]
32
+ @lambda_ = params[:lambda_]
33
+ generate_unique_primes
34
+ compute_prime_products
35
+ generate_single_prime
36
+ end
37
+
38
+ def compute_upperbound(secret)
39
+ @upperbound = ((p * m_to_s) - secret) / p
40
+ end
41
+
42
+ def compute_shares(secret)
43
+ compute_upperbound(secret)
44
+ @a = rand(1..upperbound - 1)
45
+ @y = secret + (a * p)
46
+ primes.map.with_index { |prime, i| [i, y % prime] }
47
+ end
48
+
49
+ def reconstruct_secret(selected_shares)
50
+ indices = selected_shares.map(&:first)
51
+ moduli = primes.values_at(*indices)
52
+ shares = selected_shares.map(&:last)
53
+ y = crt(moduli, shares)
54
+ y % p
55
+ end
56
+
57
+ def generate_primes
58
+ @primes = random_distinct_numbers("prime", total_shares, lambda_)
59
+ @primes.sort!
60
+ end
61
+
62
+ def compute_prime_products
63
+ @m_r = primes[0..threshold - 1].inject(:*)
64
+ @m_to_s = 1
65
+ (0..secrecy - 1).each do |i|
66
+ @m_to_s *= primes[total_shares - (i + 2) + 1]
67
+ end
68
+ end
69
+
70
+ def generate_single_prime
71
+ bits = lambda_
72
+ @p = random_prime(lambda_)
73
+ while m_r <= (k_add + 1) * ((@p * m_to_s)**(k_mul + 1))
74
+ bits -= 1
75
+ @p = random_prime(bits)
76
+ end
77
+ end
78
+
79
+ def generate_unique_primes
80
+ @primes = [random_prime(lambda_)]
81
+ while primes.uniq.size < total_shares
82
+ prime = random_prime(lambda_)
83
+ @primes << prime unless @primes.include?(prime) && (@primes + [prime]).reduce(1, :gcd) != 1
84
+ end
85
+ @primes = @primes.uniq.sort
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sharing
4
+ module Polynomial
5
+ module Shamir
6
+ # first supported version of Shamir secret sharing scheme
7
+ class V1
8
+ include HenselCode::Tools
9
+ extend HenselCode::Tools
10
+ include Tools
11
+ extend Tools
12
+
13
+ attr_accessor :lambda_, :p, :total_shares, :threshold
14
+
15
+ def self.add(shares1, shares2, prime)
16
+ shares1.zip(shares2).map { |s| [s[0][0], (s[0][1] + s[1][1]) % prime] }
17
+ end
18
+
19
+ def self.sub(shares1, shares2, prime)
20
+ shares1.zip(shares2).map { |s| [s[0][0], (s[0][1] - s[1][1]) % prime] }
21
+ end
22
+
23
+ def self.smul(shares, scalar, prime)
24
+ shares.map { |s| [s[0], (s[1] * scalar) % prime] }
25
+ end
26
+
27
+ def self.sdiv(shares, scalar, prime)
28
+ shares.map { |s| [s[0], (s[1] * mod_inverse(scalar, prime)) % prime] }
29
+ end
30
+
31
+ def self.generate_random_coefficients(total_shares, lambda_)
32
+ random_distinct_numbers("integer", total_shares - 1, lambda_ - 1)
33
+ end
34
+
35
+ def self.create_shares(secret, total_shares, lambda_, prime)
36
+ random_coefficients = generate_random_coefficients(total_shares, lambda_)
37
+ (1..total_shares).map.with_index { |x, i| [i + 1, f(x, secret, random_coefficients) % prime] }
38
+ end
39
+
40
+ def initialize(params = {})
41
+ @lambda_ = params[:lambda_]
42
+ @total_shares = params[:total_shares]
43
+ @threshold = params[:threshold]
44
+ generate_prime
45
+ end
46
+
47
+ def create_shares(secret)
48
+ random_coefficients = generate_random_coefficients
49
+ (1..total_shares).map.with_index { |x, i| [i + 1, f(x, secret, random_coefficients) % p] }
50
+ end
51
+
52
+ def reconstruct_secret(points)
53
+ xs = points.map(&:first)
54
+ ys = points.map(&:last)
55
+ l0s = lagrange_basis_polynomial(xs)
56
+ l0s.zip(ys).map { |l, y| l * y }.sum % p
57
+ end
58
+
59
+ private
60
+
61
+ def generate_prime
62
+ @p = random_prime(lambda_)
63
+ end
64
+
65
+ def generate_random_coefficients
66
+ random_distinct_numbers("integer", total_shares - 1, lambda_ - 1)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sharing
4
+ module Polynomial
5
+ # helper tool for computations with polynomials
6
+ module Tools
7
+ def lagrange_basis_polynomial_inner_loop(index, xs_)
8
+ product = 1
9
+ (0..xs_.size - 1).reject { |l| l == index }.each do |j|
10
+ product *= Rational(-xs_[j], xs_[index] - xs_[j])
11
+ end
12
+ product
13
+ end
14
+
15
+ def lagrange_basis_polynomial(xs_)
16
+ (0..xs_.size - 1).map { |i| lagrange_basis_polynomial_inner_loop(i, xs_) }
17
+ end
18
+
19
+ def f(poly_var, secret, random_coefficients)
20
+ secret + random_coefficients.each_with_index.inject(0) { |sum, (c, i)| sum + (c * (poly_var**(i + 1))) }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sharing
4
+ VERSION = "0.1.0"
5
+ end
data/lib/sharing.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prime"
4
+ require_relative "sharing/version"
5
+ require "hensel_code"
6
+
7
+ # module secret sharing
8
+ module Sharing
9
+ class Error < StandardError; end
10
+
11
+ # autoload :CRTAsmuthBloomV2, "sharing/crt_asmuth_bloom_v2"
12
+
13
+ # module for polynomial-based features
14
+ module Polynomial
15
+ autoload :Tools, "sharing/polynomial/tools"
16
+ module Shamir
17
+ autoload :V1, "sharing/polynomial/shamir/v1"
18
+ end
19
+ end
20
+
21
+ module CRT
22
+ module AsmuthBloom
23
+ autoload :V2, "sharing/crt/asmuth-bloom/v2"
24
+ end
25
+ end
26
+ end
data/sharing.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/sharing/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sharing"
7
+ spec.version = Sharing::VERSION
8
+ spec.authors = ["David William Silva"]
9
+ spec.email = ["contact@davidwsilva.com"]
10
+
11
+ spec.summary = "Secret sharing with Ruby."
12
+ spec.description = "Secret sharing with Ruby."
13
+ spec.homepage = "https://github.com/davidwilliam/sharing"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.6.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/davidwilliam/sharing"
19
+ spec.metadata["changelog_uri"] = "https://github.com/davidwilliam/sharing/blob/main/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ spec.add_dependency "hensel_code", "~> 0.3.0"
34
+ spec.add_dependency "prime", "~> 0.1.2"
35
+
36
+ # For more information and examples about making a new gem, check out our
37
+ # guide at: https://bundler.io/guides/creating_gem.html
38
+ spec.metadata["rubygems_mfa_required"] = "true"
39
+ end
@@ -0,0 +1,4 @@
1
+ module SecretSharing
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sharing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David William Silva
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-03-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hensel_code
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: prime
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.2
41
+ description: Secret sharing with Ruby.
42
+ email:
43
+ - contact@davidwsilva.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".rubocop.yml"
49
+ - CHANGELOG.md
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - bin/console
56
+ - bin/setup
57
+ - lib/sharing.rb
58
+ - lib/sharing/crt/asmuth-bloom/v2.rb
59
+ - lib/sharing/polynomial/shamir/v1.rb
60
+ - lib/sharing/polynomial/tools.rb
61
+ - lib/sharing/version.rb
62
+ - sharing.gemspec
63
+ - sig/secret_sharing.rbs
64
+ homepage: https://github.com/davidwilliam/sharing
65
+ licenses:
66
+ - MIT
67
+ metadata:
68
+ homepage_uri: https://github.com/davidwilliam/sharing
69
+ source_code_uri: https://github.com/davidwilliam/sharing
70
+ changelog_uri: https://github.com/davidwilliam/sharing/blob/main/CHANGELOG.md
71
+ rubygems_mfa_required: 'true'
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 2.6.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 3.3.3
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Secret sharing with Ruby.
91
+ test_files: []