secretsharing 0.3 → 1.0.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/.coco.yml +6 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +29 -0
- data/.travis.yml +16 -0
- data/CHANGES +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +202 -0
- data/README.md +286 -0
- data/Rakefile +12 -0
- data/SIGNED.md +99 -0
- data/bin/secretsharing +111 -0
- data/gemfiles/Gemfile.ci +7 -0
- data/lib/secretsharing.rb +25 -0
- data/lib/secretsharing/shamir.rb +91 -293
- data/lib/secretsharing/shamir/container.rb +93 -0
- data/lib/secretsharing/shamir/secret.rb +123 -0
- data/lib/secretsharing/shamir/share.rb +156 -0
- data/lib/secretsharing/version.rb +20 -0
- data/secretsharing.gemspec +47 -0
- data/spec/shamir_container_spec.rb +373 -0
- data/spec/shamir_secret_spec.rb +208 -0
- data/spec/shamir_share_spec.rb +59 -0
- data/spec/shamir_spec.rb +35 -0
- data/spec/spec_helper.rb +33 -0
- metadata +204 -57
- data/README +0 -67
- data/test/test_shamir.rb +0 -161
data/Rakefile
ADDED
data/SIGNED.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
##### Signed by https://keybase.io/grempe
|
2
|
+
```
|
3
|
+
-----BEGIN PGP SIGNATURE-----
|
4
|
+
|
5
|
+
iQIcBAABCgAGBQJUtXiuAAoJEOcWanfPl9CRG6cP/3LrrQItO+olbfYvvp+bFa54
|
6
|
+
hiK3g6IGYCKAAlocHVCzxNnEczAqCa3/HASkh96RIs0LXznKcFAIjgs/uwZs/Ahh
|
7
|
+
WYxjI9iPEyJyWffFzDIvA//A2xJCxuzupLc3yqGCTGCsvEf7yVN5fcVe8svzq6ZG
|
8
|
+
k4BuNzPLONCuzXrtmd7W15F+6M5PD67Rcs+gcHNuZEZOsXnJ3bRsu8GS8LucsxOK
|
9
|
+
SKsSwLpgJ6vAt3Ef3LSI7SQVMeRRtvgKi2iii/zGwHqRPPoqilpoeLNLlhtTX5Ee
|
10
|
+
KjfuW9qCU3zEfUs36J0JNtsf1fe0UmdiVvuRtWyM8ONHLJFP4394EwwA/A2QEYil
|
11
|
+
EYOEd8XkIcIh6lAMAKCNP22gHoJ5ezzlQ/Fk8tMGPlG+C0JVpYzf7+kzlL7iEvG/
|
12
|
+
y1YC16Cob2whgoLSvt4W7DFADwPm0WRsrdr0QPi0saM60w0fc9+6s8n6YrQEF/rv
|
13
|
+
vrNbwR7nTT0Hgxqy7F0C6N9l6mS3UOuJvbM2h3QKKI/N7aDZbD4v/SYZjQTD5aXQ
|
14
|
+
ZVV8KJxykxdHTNlsaUJptO4QFNfnWv0gWc25nDLQ+Io6v3rtV4wYf7JgIRcZgJg9
|
15
|
+
L6A0B2H1+SXkin7kcxb3ixaIzk0nRTHzF5SKWxGum3Q6wc6WiNE4CcwxuH3Pq2zD
|
16
|
+
IIhFWwazaio135TTNoj4
|
17
|
+
=BtEN
|
18
|
+
-----END PGP SIGNATURE-----
|
19
|
+
|
20
|
+
```
|
21
|
+
|
22
|
+
<!-- END SIGNATURES -->
|
23
|
+
|
24
|
+
### Begin signed statement
|
25
|
+
|
26
|
+
#### Expect
|
27
|
+
|
28
|
+
```
|
29
|
+
size exec file contents
|
30
|
+
./
|
31
|
+
96 .coco.yml 7c6c72d0ace59753d384d977006da83a0b7bf5333468047529f3e92b4b32b069
|
32
|
+
168 .gitignore cbf96ae3fecba78c775adf62469ecb1161fabf7cd336d166f2b695c02ec105de
|
33
|
+
296 .rubocop.yml 9e05e042f3c9b14bc52fc7b961cebe26cffdd72f36fea16266ef7622f315e669
|
34
|
+
182 .travis.yml 79982d49c16dfef74ac82d239073627e81ab1b4cff7d97721aa1fc047ccc9007
|
35
|
+
625 CHANGES 578a7ed1fbc9a0ccdbc148aeae0ce8b3b58cb3f78e23013d8db0aaf98ade49fa
|
36
|
+
98 Gemfile 5184edc7cae42cd49ced95779a2035fa6b04edc686450034b7ed731ef2941037
|
37
|
+
11357 LICENSE.txt c81e80664649784c5927f846ba071b04acbb6afaeeea9ee737c8a4a9c8a3bc89
|
38
|
+
12398 README.md 7191bb1605c1628dc88ad14d4b846984ed6f4d24148b8c14e49fa74c59085158
|
39
|
+
205 Rakefile 52e019b00c55641f894f914df53a40d993ccb90b20731338004269292f4c5d7f
|
40
|
+
bin/
|
41
|
+
3151 x secretsharing 79efbec8ef0fc6f24417ea490defc6008ac6533ae96dabc0ab7f59c2b385d791
|
42
|
+
gemfiles/
|
43
|
+
101 Gemfile.ci 7e196ea31483bcfd25f626d3ce5eff19ee8c330c44a3ca6b80a4bd8c8aece065
|
44
|
+
lib/
|
45
|
+
secretsharing/
|
46
|
+
shamir/
|
47
|
+
3749 container.rb 37fd7db90e4f2f337db979c93b7022710d443815a0be8f71f3c642902c7cea80
|
48
|
+
4954 secret.rb 34ed54e31a4f862ea5f7df2074b7b204dc5400548a47b7f0055fa7050b7ff1e3
|
49
|
+
6017 share.rb a380d5adf64815e62ad4256596b8a05ede61a8c0e7ec1436348946edd3aaed7a
|
50
|
+
3031 shamir.rb 5702d49cc0e97202e6e0867a0f6955366d407ec4b3fe2d5989bc2259d2776f9f
|
51
|
+
706 version.rb 428fb8abe60cd3e195e87f1d5f438e03ce797de8ceb699ab18260b7a9a4d9848
|
52
|
+
863 secretsharing.rb 73deaa58299b597e0540b905f1f713dff526060a9a24c6909cdca83ff23164c4
|
53
|
+
1733 secretsharing.gemspec 00430997e55126061ec3729438a58ec01e07b07fa846467947f55cfde8285a86
|
54
|
+
spec/
|
55
|
+
12643 shamir_container_spec.rb 39f3e0393cdf50c88fae9ba4302d32dc881fc2ce3b4b8adcd97cf1fb5df026f2
|
56
|
+
7023 shamir_secret_spec.rb 67533deb4a929155197067106e4149f70bc49b626b08690e8e3611106885ef77
|
57
|
+
2719 shamir_share_spec.rb 4d2132531310348308c72af976cdc489bb59ef4b77373d5a71dd95b15f3205db
|
58
|
+
1032 shamir_spec.rb c5a7e030dad410917a7e52f46f0531a4df8d2f05019e9136a34750f97699b7e2
|
59
|
+
997 spec_helper.rb 9b634a61716596562288b4c4325e43696af87b48abcd36b34250991aa010caf9
|
60
|
+
```
|
61
|
+
|
62
|
+
#### Ignore
|
63
|
+
|
64
|
+
```
|
65
|
+
/SIGNED.md
|
66
|
+
```
|
67
|
+
|
68
|
+
#### Presets
|
69
|
+
|
70
|
+
```
|
71
|
+
git # ignore .git and anything as described by .gitignore files
|
72
|
+
dropbox # ignore .dropbox-cache and other Dropbox-related files
|
73
|
+
kb # ignore anything as described by .kbignore files
|
74
|
+
```
|
75
|
+
|
76
|
+
<!-- summarize version = 0.0.9 -->
|
77
|
+
|
78
|
+
### End signed statement
|
79
|
+
|
80
|
+
<hr>
|
81
|
+
|
82
|
+
#### Notes
|
83
|
+
|
84
|
+
With keybase you can sign any directory's contents, whether it's a git repo,
|
85
|
+
source code distribution, or a personal documents folder. It aims to replace the drudgery of:
|
86
|
+
|
87
|
+
1. comparing a zipped file to a detached statement
|
88
|
+
2. downloading a public key
|
89
|
+
3. confirming it is in fact the author's by reviewing public statements they've made, using it
|
90
|
+
|
91
|
+
All in one simple command:
|
92
|
+
|
93
|
+
```bash
|
94
|
+
keybase dir verify
|
95
|
+
```
|
96
|
+
|
97
|
+
There are lots of options, including assertions for automating your checks.
|
98
|
+
|
99
|
+
For more info, check out https://keybase.io/docs/command_line/code_signing
|
data/bin/secretsharing
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'highline/import'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'secretsharing'
|
7
|
+
rescue LoadError
|
8
|
+
require 'rubygems'
|
9
|
+
require 'secretsharing'
|
10
|
+
end
|
11
|
+
|
12
|
+
include SecretSharing::Shamir
|
13
|
+
|
14
|
+
# FIXME : Need to be able to specify bitlength of random secret
|
15
|
+
# FIXME : Need tests
|
16
|
+
# FIXME : Allow specify a fixed secret that is a num or a String
|
17
|
+
|
18
|
+
# Build up a Hash of all of the options chosen
|
19
|
+
choices = {}
|
20
|
+
|
21
|
+
say("\nShamir's Secret Sharing\n")
|
22
|
+
|
23
|
+
say("\nWould you like to 'encode' a new secret as shares, or 'decode' one from existing shares?\n")
|
24
|
+
choose do |menu|
|
25
|
+
menu.prompt = 'Action? '
|
26
|
+
|
27
|
+
menu.choice(:encode) do
|
28
|
+
choices[:action] = :encode
|
29
|
+
end
|
30
|
+
|
31
|
+
menu.choice(:decode) do
|
32
|
+
choices[:action] = :decode
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if choices[:action] == :encode
|
37
|
+
|
38
|
+
say("\nWould you like to create a 'random' secret, or will you provide a 'fixed' one?\n")
|
39
|
+
choose do |menu|
|
40
|
+
menu.prompt = 'Type? '
|
41
|
+
|
42
|
+
menu.choice(:random) do
|
43
|
+
choices[:secret_type] = :random
|
44
|
+
end
|
45
|
+
|
46
|
+
menu.choice(:fixed) do
|
47
|
+
choices[:secret_type] = :fixed
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if choices[:secret_type] == :fixed
|
52
|
+
choices[:secret_password] = ask('Enter your numeric password: ', Integer) { |q| q.validate = /^[0-9]+$/ }
|
53
|
+
end
|
54
|
+
|
55
|
+
choices[:secret_n] = ask('How many total shares (n) do you want to distribute? ', Integer) { |q| q.in = 2..512 }
|
56
|
+
choices[:secret_k] = ask('How many of the total shares (k) are required to reveal the secret? ', Integer) { |q| q.in = 2..512 }
|
57
|
+
|
58
|
+
@c = SecretSharing::Shamir::Container.new(choices[:secret_n], choices[:secret_k])
|
59
|
+
|
60
|
+
if choices[:secret_type] == :fixed
|
61
|
+
@c.secret = SecretSharing::Shamir::Secret.new(:secret => OpenSSL::BN.new(choices[:secret_password].to_s))
|
62
|
+
else
|
63
|
+
@c.secret = SecretSharing::Shamir::Secret.new
|
64
|
+
end
|
65
|
+
|
66
|
+
say("\n========================================\n")
|
67
|
+
say("Encoded Secret:\n\n")
|
68
|
+
say("(k) Value: #{choices[:secret_k]}\n")
|
69
|
+
say("(n) Value: #{choices[:secret_n]}\n")
|
70
|
+
say("\n")
|
71
|
+
say("Secret (Bignum): \n")
|
72
|
+
say(@c.secret.secret.to_s)
|
73
|
+
say("\n")
|
74
|
+
say("Secret (Base64 Compacted & URL Safe): \n")
|
75
|
+
say(@c.secret.to_s)
|
76
|
+
say("\n")
|
77
|
+
say("Secret has valid_hmac? \n")
|
78
|
+
say(@c.secret.valid_hmac?.to_s + "\n")
|
79
|
+
say("\n")
|
80
|
+
say("Shares:\n")
|
81
|
+
@c.shares.each { |s| say s.to_s }
|
82
|
+
say("\n========================================\n")
|
83
|
+
|
84
|
+
elsif choices[:action] == :decode
|
85
|
+
say("\n")
|
86
|
+
choices[:secret_k] = ask('How many of shares (k) are required to reveal this secret? ', Integer) { |q| q.in = 2..512 }
|
87
|
+
|
88
|
+
@c = SecretSharing::Shamir::Container.new(choices[:secret_k])
|
89
|
+
|
90
|
+
say("\n")
|
91
|
+
shares = ask("Enter the '#{choices[:secret_k]}' shares one at a time with a RETURN after each:", lambda { |ans| ans =~ /^-?\d+$/ ? Integer(ans) : ans }) do |q|
|
92
|
+
q.gather = choices[:secret_k]
|
93
|
+
end
|
94
|
+
|
95
|
+
shares.map { |s| @c << s }
|
96
|
+
|
97
|
+
say("\n")
|
98
|
+
if @c.secret?
|
99
|
+
say("\n========================================\n")
|
100
|
+
say("Decoded Secret:\n\n")
|
101
|
+
say("(k) Value: #{choices[:secret_k]}\n")
|
102
|
+
say("\n")
|
103
|
+
say("Secret (Bignum): \n")
|
104
|
+
say(@c.secret.secret.to_s)
|
105
|
+
say("\n")
|
106
|
+
say("Secret (Base64 Compacted & URL Safe): \n")
|
107
|
+
say(@c.secret.to_s)
|
108
|
+
say("\n")
|
109
|
+
say("\n========================================\n")
|
110
|
+
end
|
111
|
+
end
|
data/gemfiles/Gemfile.ci
ADDED
data/lib/secretsharing.rb
CHANGED
@@ -1 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Copyright 2011-2015 Glenn Rempe
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'openssl'
|
18
|
+
require 'digest/sha1'
|
19
|
+
require 'base64'
|
20
|
+
require 'multi_json'
|
21
|
+
|
22
|
+
require 'secretsharing/version'
|
1
23
|
require 'secretsharing/shamir'
|
24
|
+
require 'secretsharing/shamir/container'
|
25
|
+
require 'secretsharing/shamir/share'
|
26
|
+
require 'secretsharing/shamir/secret'
|
data/lib/secretsharing/shamir.rb
CHANGED
@@ -1,298 +1,96 @@
|
|
1
|
-
|
2
|
-
require 'digest/sha1'
|
3
|
-
require 'base64'
|
1
|
+
# -*- encoding: utf-8 -*-
|
4
2
|
|
5
|
-
|
6
|
-
# The SecretSharing::Shamir class can be used to share random
|
7
|
-
# secrets between n people, so that k < n people can recover the
|
8
|
-
# secret, but k-1 people learn nothing (in an information-theoretical
|
9
|
-
# sense) about the secret.
|
10
|
-
#
|
11
|
-
# For a theoretical background, see
|
12
|
-
# http://www.cs.tau.ac.il/~bchor/Shamir.html or
|
13
|
-
# http://en.wikipedia.org/wiki/Secret_sharing#Shamir.27s_scheme
|
14
|
-
#
|
15
|
-
# To share a secret, create a new SecretSharing::Shamir object and
|
16
|
-
# then call the create_random_secret() method. The secret is now in
|
17
|
-
# the secret attribute and the shares are an array in the shares attribute.
|
18
|
-
#
|
19
|
-
# Alternatively, you can call the set_fixed_secret() method with an
|
20
|
-
# OpenSSL::BN object (or something that can be passed to OpenSSL::BN.new)
|
21
|
-
# to set your own secret.
|
22
|
-
#
|
23
|
-
# To recover a secret, create a SecretSharing::Shamir object and
|
24
|
-
# add the necessary shares to it using the '<<' method. Once enough
|
25
|
-
# shares have been added, the secret can be recovered in the secret
|
26
|
-
# attribute.
|
27
|
-
class Shamir
|
28
|
-
attr_reader :n, :k, :secret, :secret_bitlength, :shares
|
29
|
-
|
30
|
-
DEFAULT_SECRET_BITLENGTH = 256
|
31
|
-
|
32
|
-
# To create a new SecretSharing::Shamir object, you can
|
33
|
-
# pass either just n, or k and n.
|
34
|
-
#
|
35
|
-
# For example:
|
36
|
-
# s = SecretSharing::Shamir.new(5, 3)
|
37
|
-
# to create an object for 3 out of 5 secret sharing.
|
38
|
-
#
|
39
|
-
# or
|
40
|
-
# s = SecretSharing::Shamir.new(3)
|
41
|
-
# for 3 out of 3 secret sharing.
|
42
|
-
def initialize(n, k=n)
|
43
|
-
if k > n then
|
44
|
-
raise ArgumentError, 'k must be smaller or equal than n'
|
45
|
-
end
|
46
|
-
if k < 2 then
|
47
|
-
raise ArgumentError, 'k must be greater or equal to two'
|
48
|
-
end
|
49
|
-
if n > 255 then
|
50
|
-
raise ArgumentError, 'n must be smaller than 256'
|
51
|
-
end
|
52
|
-
@n = n
|
53
|
-
@k = k
|
54
|
-
@secret = nil
|
55
|
-
@shares = []
|
56
|
-
@received_shares = []
|
57
|
-
end
|
58
|
-
|
59
|
-
# Check whether the secret is set.
|
60
|
-
def secret_set?
|
61
|
-
! @secret.nil?
|
62
|
-
end
|
63
|
-
|
64
|
-
# Create a random secret of a certain bitlength. Returns the
|
65
|
-
# secret and stores it in the 'secret' attribute.
|
66
|
-
def create_random_secret(bitlength = DEFAULT_SECRET_BITLENGTH)
|
67
|
-
raise 'secret already set' if secret_set?
|
68
|
-
raise 'max bitlength is 1024' if bitlength > 1024
|
69
|
-
@secret = get_random_number(bitlength)
|
70
|
-
@secret_bitlength = bitlength
|
71
|
-
create_shares
|
72
|
-
@secret
|
73
|
-
end
|
74
|
-
|
75
|
-
# Set the secret to a fixed OpenSSL::BN value. Stores it
|
76
|
-
# in the 'secret' attribute, creates the corresponding shares and
|
77
|
-
# returns the secret
|
78
|
-
def set_fixed_secret(secret)
|
79
|
-
raise 'secret already set' if secret_set?
|
80
|
-
if secret.class != OpenSSL::BN then
|
81
|
-
# create OpenSSL bignum
|
82
|
-
secret = OpenSSL::BN.new(secret)
|
83
|
-
end
|
84
|
-
raise 'max bitlength is 1024' if secret.num_bits > 1024
|
85
|
-
@secret = secret
|
86
|
-
@secret_bitlength = secret.num_bits
|
87
|
-
create_shares
|
88
|
-
@secret
|
89
|
-
end
|
90
|
-
|
91
|
-
# The secret in a password representation (Base64-encoded)
|
92
|
-
def secret_password
|
93
|
-
if ! secret_set? then
|
94
|
-
raise "Secret not (yet) set."
|
95
|
-
end
|
96
|
-
Base64.encode64([@secret.to_s(16)].pack('h*')).split("\n").join
|
97
|
-
end
|
98
|
-
|
99
|
-
# Add a secret share to the object. Accepts either a
|
100
|
-
# SecretSharing::Shamir::Share instance or a string representing one.
|
101
|
-
# Returns true if enough shares have been added to recover the secret,
|
102
|
-
# false otherweise.
|
103
|
-
def <<(share)
|
104
|
-
# convert from string if needed
|
105
|
-
if share.class != SecretSharing::Shamir::Share then
|
106
|
-
if share.class == String then
|
107
|
-
share = SecretSharing::Shamir::Share.from_string(share)
|
108
|
-
else
|
109
|
-
raise ArgumentError 'SecretSharing::Shamir::Share ' \
|
110
|
-
+ 'or String needed'
|
111
|
-
end
|
112
|
-
end
|
113
|
-
if @received_shares.include? share then
|
114
|
-
raise 'share has already been added'
|
115
|
-
end
|
116
|
-
if @received_shares.length == @k then
|
117
|
-
raise 'we already have enough shares, no need to add more'
|
118
|
-
end
|
119
|
-
@received_shares << share
|
120
|
-
if @received_shares.length == @k then
|
121
|
-
recover_secret
|
122
|
-
return true
|
123
|
-
end
|
124
|
-
false
|
125
|
-
end
|
126
|
-
|
127
|
-
# Computes the smallest prime of a given bitlength. Uses prime_fasttest
|
128
|
-
# from the OpenSSL library with 20 attempts to be compatible to openssl
|
129
|
-
# prime, which is used in the OpenXPKI::Crypto::Secret::Split library.
|
130
|
-
def self.smallest_prime_of_bitlength(bitlength)
|
131
|
-
# start with 2^bit_length + 1
|
132
|
-
test_prime = OpenSSL::BN.new((2**bitlength + 1).to_s)
|
133
|
-
prime_found = false
|
134
|
-
while (! prime_found) do
|
135
|
-
# prime_fasttest? 20 do be compatible to
|
136
|
-
# openssl prime, which is used in
|
137
|
-
# OpenXPKI::Crypto::Secret::Split
|
138
|
-
prime_found = test_prime.prime_fasttest? 20
|
139
|
-
test_prime += 2
|
140
|
-
end
|
141
|
-
test_prime
|
142
|
-
end
|
143
|
-
|
144
|
-
private
|
145
|
-
# Creates a random number of a certain bitlength, optionally ensuring
|
146
|
-
# the bitlength by setting the highest bit to 1.
|
147
|
-
def get_random_number(bitlength, highest_bit_one = true)
|
148
|
-
byte_length = (bitlength / 8.0).ceil
|
149
|
-
rand_hex = OpenSSL::Random.random_bytes(byte_length).each_byte. \
|
150
|
-
to_a.map { |a| "%02x" % a }.join('')
|
151
|
-
rand = OpenSSL::BN.new(rand_hex, 16)
|
152
|
-
begin
|
153
|
-
rand.mask_bits!(bitlength)
|
154
|
-
rescue OpenSSL::BNError
|
155
|
-
# never mind if there was an error, this just means
|
156
|
-
# rand was already smaller than 2^bitlength - 1
|
157
|
-
end
|
158
|
-
if highest_bit_one then
|
159
|
-
rand.set_bit!(bitlength)
|
160
|
-
end
|
161
|
-
rand
|
162
|
-
end
|
3
|
+
# Copyright 2011-2015 Glenn Rempe
|
163
4
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
@coefficients = []
|
168
|
-
@coefficients[0] = @secret
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
169
8
|
|
170
|
-
|
171
|
-
next_nibble_bitlength = @secret_bitlength + \
|
172
|
-
(4 - (@secret_bitlength % 4))
|
173
|
-
prime_bitlength = next_nibble_bitlength + 1
|
174
|
-
@prime = self.class.smallest_prime_of_bitlength(prime_bitlength)
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
175
10
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
180
16
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
"got #{computed_checksum}"
|
262
|
-
end
|
263
|
-
prime = SecretSharing::Shamir. \
|
264
|
-
smallest_prime_of_bitlength(prime_bitlength)
|
265
|
-
self.new(x, OpenSSL::BN.new(p_x_str, 16), prime, prime_bitlength)
|
266
|
-
end
|
267
|
-
|
268
|
-
# A string representation of the share, that can for example be
|
269
|
-
# distributed in printed form.
|
270
|
-
# The string is an uppercase hexadecimal string of the following
|
271
|
-
# format: ABBC*DDDDEEEE, where
|
272
|
-
# * A (the first nibble) is the version number of the format, currently
|
273
|
-
# fixed to 0.
|
274
|
-
# * B (the next byte, two hex characters) is the x coordinate of the
|
275
|
-
# point on the polynomial.
|
276
|
-
# * C (the next variable length of bytes) is the y coordinate of the
|
277
|
-
# point on the polynomial.
|
278
|
-
# * D (the next two bytes, four hex characters) is the two highest
|
279
|
-
# bytes of the SHA1 hash on the string representing the y coordinate,
|
280
|
-
# it is used as a checksum to guard against typos
|
281
|
-
# * E (the next two bytes, four hex characters) is the bitlength of the
|
282
|
-
# prime number in nibbles.
|
283
|
-
def to_s
|
284
|
-
# bitlength in nibbles to save space
|
285
|
-
prime_nibbles = (@prime_bitlength - 1) / 4
|
286
|
-
p_x = ("%x" % @y).upcase
|
287
|
-
FORMAT_VERSION + ("%02x" % @x).upcase \
|
288
|
-
+ p_x \
|
289
|
-
+ Digest::SHA1.hexdigest(p_x)[0,4].upcase \
|
290
|
-
+ ("%02x" % prime_nibbles).upcase
|
291
|
-
end
|
292
|
-
|
293
|
-
# Shares are equal if their string representation is the same.
|
294
|
-
def ==(share)
|
295
|
-
share.to_s == self.to_s
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|
17
|
+
module SecretSharing
|
18
|
+
# Module for common methods shared across Container, Secret, or Share
|
19
|
+
module Shamir
|
20
|
+
# FIXME : Needs focused tests
|
21
|
+
# Creates a random number of a certain bitlength, optionally ensuring
|
22
|
+
# the bitlength by setting the highest bit to 1.
|
23
|
+
def get_random_number(bitlength)
|
24
|
+
byte_length = (bitlength / 8.0).ceil
|
25
|
+
rand_hex = OpenSSL::Random.random_bytes(byte_length).each_byte.to_a.map { |a| sprintf('%02x', a) }.join('')
|
26
|
+
rand = OpenSSL::BN.new(rand_hex, 16)
|
27
|
+
|
28
|
+
begin
|
29
|
+
rand.mask_bits!(bitlength)
|
30
|
+
rescue OpenSSL::BNError
|
31
|
+
# never mind if there was an error, this just means
|
32
|
+
# rand was already smaller than 2^bitlength - 1
|
33
|
+
end
|
34
|
+
|
35
|
+
rand.set_bit!(bitlength)
|
36
|
+
rand
|
37
|
+
end
|
38
|
+
|
39
|
+
# FIXME : Needs focused tests
|
40
|
+
|
41
|
+
# Evaluate the polynomial at x.
|
42
|
+
def evaluate_polynomial_at(x, coefficients, prime)
|
43
|
+
result = OpenSSL::BN.new('0')
|
44
|
+
|
45
|
+
coefficients.each_with_index do |c, i|
|
46
|
+
result += c * OpenSSL::BN.new(x.to_s)**i
|
47
|
+
result %= prime
|
48
|
+
end
|
49
|
+
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
# FIXME : Needs focused tests
|
54
|
+
|
55
|
+
# Part of the Lagrange interpolation.
|
56
|
+
# This is l_j(0), i.e.
|
57
|
+
# \prod_{x_j \neq x_i} \frac{-x_i}{x_j - x_i}
|
58
|
+
# for more information compare Wikipedia:
|
59
|
+
# http://en.wikipedia.org/wiki/Lagrange_form
|
60
|
+
def lagrange(x, shares)
|
61
|
+
prime = shares.first.prime
|
62
|
+
other_shares = shares.reject { |s| s.x == x }
|
63
|
+
|
64
|
+
results = other_shares.map do |s|
|
65
|
+
minus_xi = OpenSSL::BN.new("#{-s.x}")
|
66
|
+
one_over_xj_minus_xi = OpenSSL::BN.new("#{x - s.x}").mod_inverse(prime)
|
67
|
+
minus_xi.mod_mul(one_over_xj_minus_xi, prime)
|
68
|
+
end
|
69
|
+
|
70
|
+
results.reduce { |a, e| a.mod_mul(e, prime) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# FIXME : Needs focused tests
|
74
|
+
|
75
|
+
# Backported for Ruby 1.8.7, REE, JRuby, Rubinious
|
76
|
+
def usafe_decode64(str)
|
77
|
+
str = str.strip
|
78
|
+
return Base64.urlsafe_decode64(str) if Base64.respond_to?(:urlsafe_decode64)
|
79
|
+
|
80
|
+
if str.include?('\n')
|
81
|
+
fail(ArgumentError, 'invalid base64')
|
82
|
+
else
|
83
|
+
Base64.decode64(str)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# FIXME : Needs focused tests
|
88
|
+
|
89
|
+
# Backported for Ruby 1.8.7, REE, JRuby, Rubinious
|
90
|
+
def usafe_encode64(bin)
|
91
|
+
bin = bin.strip
|
92
|
+
return Base64.urlsafe_encode64(bin) if Base64.respond_to?(:urlsafe_encode64)
|
93
|
+
Base64.encode64(bin).tr("\n", '')
|
94
|
+
end
|
95
|
+
end # module Shamir
|
96
|
+
end # module SecretSharing
|