secretsharing 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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");
@@ -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 SecretSharing::Shamir::Share
80
- # instance or a string representing one. Returns true if enough shares have
81
- # been added to recover the secret, false otherweise.
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 or String needed'
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 OpenXPKI::Crypto::Secret::Split
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 the
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.to_a.map { |a| "%02x" % a }.join('')
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 + (4 - (@secret_bitlength % 4))
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}, got #{computed_checksum}"
260
+ raise "invalid checksum. expected #{checksum}, " + \
261
+ "got #{computed_checksum}"
236
262
  end
237
- prime = SecretSharing::Shamir.smallest_prime_of_bitlength(prime_bitlength)
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 point
248
- # on the polynomial.
249
- # * C (the next variable length of bytes) is the y coordinate of the point
250
- # on the polynomial.
251
- # * D (the next two bytes, four hex characters) is the two highest bytes of
252
- # the SHA1 hash on the string representing the y coordinate, it is used as
253
- # a checksum to guard against typos
254
- # * E (the next two bytes, four hex characters) is the bitlength of the prime
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: 15
4
+ hash: 13
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- version: "0.2"
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: 2010-09-26 00:00:00 +02:00
17
+ date: 2011-06-27 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies: []
20
20