secretsharing 0.2 → 0.3
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.
- data/README +1 -4
- data/lib/secretsharing/shamir.rb +52 -20
- data/test/test_shamir.rb +51 -0
- metadata +4 -4
data/README
CHANGED
@@ -43,11 +43,8 @@
|
|
43
43
|
s2 << s.shares[4]
|
44
44
|
puts s2.secret
|
45
45
|
|
46
|
-
== Future Plans
|
47
|
-
Add support for using your own instead of a random secret.
|
48
|
-
|
49
46
|
== Copyright
|
50
|
-
(c) 2010 Alexander Klink
|
47
|
+
(c) 2010-2011 Alexander Klink
|
51
48
|
|
52
49
|
== License
|
53
50
|
Licensed under the Apache License, Version 2.0 (the "License");
|
data/lib/secretsharing/shamir.rb
CHANGED
@@ -16,6 +16,10 @@ module SecretSharing
|
|
16
16
|
# then call the create_random_secret() method. The secret is now in
|
17
17
|
# the secret attribute and the shares are an array in the shares attribute.
|
18
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
|
+
#
|
19
23
|
# To recover a secret, create a SecretSharing::Shamir object and
|
20
24
|
# add the necessary shares to it using the '<<' method. Once enough
|
21
25
|
# shares have been added, the secret can be recovered in the secret
|
@@ -68,6 +72,22 @@ module SecretSharing
|
|
68
72
|
@secret
|
69
73
|
end
|
70
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
|
+
|
71
91
|
# The secret in a password representation (Base64-encoded)
|
72
92
|
def secret_password
|
73
93
|
if ! secret_set? then
|
@@ -76,16 +96,18 @@ module SecretSharing
|
|
76
96
|
Base64.encode64([@secret.to_s(16)].pack('h*')).split("\n").join
|
77
97
|
end
|
78
98
|
|
79
|
-
# Add a secret share to the object. Accepts either a
|
80
|
-
# instance or a string representing one.
|
81
|
-
# been added to recover the secret,
|
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.
|
82
103
|
def <<(share)
|
83
104
|
# convert from string if needed
|
84
105
|
if share.class != SecretSharing::Shamir::Share then
|
85
106
|
if share.class == String then
|
86
107
|
share = SecretSharing::Shamir::Share.from_string(share)
|
87
108
|
else
|
88
|
-
raise ArgumentError 'SecretSharing::Shamir::Share
|
109
|
+
raise ArgumentError 'SecretSharing::Shamir::Share ' \
|
110
|
+
+ 'or String needed'
|
89
111
|
end
|
90
112
|
end
|
91
113
|
if @received_shares.include? share then
|
@@ -111,7 +133,8 @@ module SecretSharing
|
|
111
133
|
prime_found = false
|
112
134
|
while (! prime_found) do
|
113
135
|
# prime_fasttest? 20 do be compatible to
|
114
|
-
# openssl prime, which is used in
|
136
|
+
# openssl prime, which is used in
|
137
|
+
# OpenXPKI::Crypto::Secret::Split
|
115
138
|
prime_found = test_prime.prime_fasttest? 20
|
116
139
|
test_prime += 2
|
117
140
|
end
|
@@ -119,11 +142,12 @@ module SecretSharing
|
|
119
142
|
end
|
120
143
|
|
121
144
|
private
|
122
|
-
# Creates a random number of a certain bitlength, optionally ensuring
|
123
|
-
# bitlength by setting the highest bit to 1.
|
145
|
+
# Creates a random number of a certain bitlength, optionally ensuring
|
146
|
+
# the bitlength by setting the highest bit to 1.
|
124
147
|
def get_random_number(bitlength, highest_bit_one = true)
|
125
148
|
byte_length = (bitlength / 8.0).ceil
|
126
|
-
rand_hex = OpenSSL::Random.random_bytes(byte_length).each_byte.
|
149
|
+
rand_hex = OpenSSL::Random.random_bytes(byte_length).each_byte. \
|
150
|
+
to_a.map { |a| "%02x" % a }.join('')
|
127
151
|
rand = OpenSSL::BN.new(rand_hex, 16)
|
128
152
|
begin
|
129
153
|
rand.mask_bits!(bitlength)
|
@@ -144,7 +168,8 @@ module SecretSharing
|
|
144
168
|
@coefficients[0] = @secret
|
145
169
|
|
146
170
|
# round up to next nibble
|
147
|
-
next_nibble_bitlength = @secret_bitlength +
|
171
|
+
next_nibble_bitlength = @secret_bitlength + \
|
172
|
+
(4 - (@secret_bitlength % 4))
|
148
173
|
prime_bitlength = next_nibble_bitlength + 1
|
149
174
|
@prime = self.class.smallest_prime_of_bitlength(prime_bitlength)
|
150
175
|
|
@@ -232,9 +257,11 @@ module SecretSharing
|
|
232
257
|
checksum = string[-6, 4]
|
233
258
|
computed_checksum = Digest::SHA1.hexdigest(p_x_str)[0,4].upcase
|
234
259
|
if checksum != computed_checksum then
|
235
|
-
raise "invalid checksum. expected #{checksum},
|
260
|
+
raise "invalid checksum. expected #{checksum}, " + \
|
261
|
+
"got #{computed_checksum}"
|
236
262
|
end
|
237
|
-
prime = SecretSharing::Shamir.
|
263
|
+
prime = SecretSharing::Shamir. \
|
264
|
+
smallest_prime_of_bitlength(prime_bitlength)
|
238
265
|
self.new(x, OpenSSL::BN.new(p_x_str, 16), prime, prime_bitlength)
|
239
266
|
end
|
240
267
|
|
@@ -244,15 +271,15 @@ module SecretSharing
|
|
244
271
|
# format: ABBC*DDDDEEEE, where
|
245
272
|
# * A (the first nibble) is the version number of the format, currently
|
246
273
|
# fixed to 0.
|
247
|
-
# * B (the next byte, two hex characters) is the x coordinate of the
|
248
|
-
# on the polynomial.
|
249
|
-
# * C (the next variable length of bytes) is the y coordinate of the
|
250
|
-
# on the polynomial.
|
251
|
-
# * D (the next two bytes, four hex characters) is the two highest
|
252
|
-
# the SHA1 hash on the string representing the y coordinate,
|
253
|
-
# a checksum to guard against typos
|
254
|
-
# * E (the next two bytes, four hex characters) is the bitlength of the
|
255
|
-
# number in nibbles.
|
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.
|
256
283
|
def to_s
|
257
284
|
# bitlength in nibbles to save space
|
258
285
|
prime_nibbles = (@prime_bitlength - 1) / 4
|
@@ -262,5 +289,10 @@ module SecretSharing
|
|
262
289
|
+ Digest::SHA1.hexdigest(p_x)[0,4].upcase \
|
263
290
|
+ ("%02x" % prime_nibbles).upcase
|
264
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
|
265
297
|
end
|
266
298
|
end
|
data/test/test_shamir.rb
CHANGED
@@ -37,6 +37,33 @@ class TestShamir < Test::Unit::TestCase
|
|
37
37
|
assert_equal(512, s2.secret_bitlength)
|
38
38
|
end
|
39
39
|
|
40
|
+
def test_set_fixed_secret
|
41
|
+
s = SecretSharing::Shamir.new(5)
|
42
|
+
s.set_fixed_secret(OpenSSL::BN.new('12345678901234567890'))
|
43
|
+
assert(s.secret_set?)
|
44
|
+
assert_not_nil(s.secret)
|
45
|
+
assert_not_nil(s.shares)
|
46
|
+
assert_equal(Array, s.shares.class)
|
47
|
+
assert_equal(5, s.shares.length)
|
48
|
+
assert_equal(SecretSharing::Shamir::Share, s.shares[0].class)
|
49
|
+
assert_equal(64, s.secret_bitlength)
|
50
|
+
|
51
|
+
# can only be called once
|
52
|
+
assert_raise( RuntimeError) {
|
53
|
+
s.set_fixed_secret(OpenSSL::BN.new('12345678901234567891')) }
|
54
|
+
|
55
|
+
# test using string as parameter instead of OpenSSL::BN instance
|
56
|
+
s2 = SecretSharing::Shamir.new(5)
|
57
|
+
s2.set_fixed_secret('12345678901234567890')
|
58
|
+
assert(s2.secret_set?)
|
59
|
+
assert_not_nil(s2.secret)
|
60
|
+
assert_not_nil(s2.shares)
|
61
|
+
assert_equal(Array, s2.shares.class)
|
62
|
+
assert_equal(5, s2.shares.length)
|
63
|
+
assert_equal(SecretSharing::Shamir::Share, s2.shares[0].class)
|
64
|
+
assert_equal(64, s2.secret_bitlength)
|
65
|
+
end
|
66
|
+
|
40
67
|
def test_recover_secret_k_eq_n
|
41
68
|
s = SecretSharing::Shamir.new(5)
|
42
69
|
s.create_random_secret()
|
@@ -59,6 +86,30 @@ class TestShamir < Test::Unit::TestCase
|
|
59
86
|
assert_equal(s.secret, s2.secret)
|
60
87
|
end
|
61
88
|
|
89
|
+
def test_recover_secret_k_eq_n_fixed_secret
|
90
|
+
s = SecretSharing::Shamir.new(5)
|
91
|
+
secret = OpenSSL::BN.new('1234567890123456789012345678901234567890')
|
92
|
+
|
93
|
+
s.set_fixed_secret(secret)
|
94
|
+
|
95
|
+
s2 = SecretSharing::Shamir.new(5)
|
96
|
+
s2 << s.shares[0]
|
97
|
+
assert(! s2.secret_set?)
|
98
|
+
assert_nil(s2.secret)
|
99
|
+
# adding the same share raises an error
|
100
|
+
assert_raise( RuntimeError ) { s2 << s.shares[0] }
|
101
|
+
# add more shares
|
102
|
+
s2 << s.shares[1]
|
103
|
+
assert(! s2.secret_set?)
|
104
|
+
s2 << s.shares[2]
|
105
|
+
assert(! s2.secret_set?)
|
106
|
+
s2 << s.shares[3]
|
107
|
+
assert(! s2.secret_set?)
|
108
|
+
s2 << s.shares[4]
|
109
|
+
assert(s2.secret_set?)
|
110
|
+
assert_equal(secret, s2.secret)
|
111
|
+
end
|
112
|
+
|
62
113
|
def test_recover_secret_k_eq_n_strings
|
63
114
|
s = SecretSharing::Shamir.new(2)
|
64
115
|
s.create_random_secret()
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: secretsharing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 13
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
8
|
+
- 3
|
9
|
+
version: "0.3"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Alexander Klink
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-06-27 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|