sharing 0.1.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 +7 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +79 -0
- data/LICENSE.txt +21 -0
- data/README.md +242 -0
- data/Rakefile +16 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/sharing/crt/asmuth-bloom/v2.rb +90 -0
- data/lib/sharing/polynomial/shamir/v1.rb +71 -0
- data/lib/sharing/polynomial/tools.rb +24 -0
- data/lib/sharing/version.rb +5 -0
- data/lib/sharing.rb +26 -0
- data/sharing.gemspec +39 -0
- data/sig/secret_sharing.rbs +4 -0
- metadata +91 -0
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
data/CHANGELOG.md
ADDED
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,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
|
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
|
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: []
|