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 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