secretsharing 0.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|