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