tss 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ 2.2.4
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.2.0
5
+ - 2.3.0
6
+ before_install: gem install bundler -v 1.11.2
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at glenn@rempe.us. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tss.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Glenn Rempe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,590 @@
1
+ # TSS - Threshold Secret Sharing
2
+
3
+ [![Build Status](https://travis-ci.org/grempe/tss-rb.svg?branch=master)](https://travis-ci.org/grempe/tss-rb)
4
+ [![Coverage Status](https://coveralls.io/repos/github/grempe/tss-rb/badge.svg?branch=master)](https://coveralls.io/github/grempe/tss-rb?branch=master)
5
+ [![Code Climate](https://codeclimate.com/github/grempe/tss-rb/badges/gpa.svg)](https://codeclimate.com/github/grempe/tss-rb)
6
+
7
+ ## WARNING : PRE-ALPHA CODE
8
+
9
+ This code is currently a work in progress and is not yet ready for production
10
+ use. The API, input and output formats, and other aspects are likely to change
11
+ before release. There has been no security review of this code.
12
+
13
+ ## About TSS
14
+
15
+ This Ruby gem implements Threshold Secret Sharing, as specified in
16
+ the Network Working Group Internet-Draft submitted by D. McGrew
17
+ ([draft-mcgrew-tss-03.txt](http://tools.ietf.org/html/draft-mcgrew-tss-03)).
18
+
19
+ Threshold Secret Sharing (TSS) provides a way to generate `N` shares
20
+ from a value, so that any `M` of those shares can be used to
21
+ reconstruct the original value, but any `M-1` shares provide no
22
+ information about that value. This method can provide shared access
23
+ control on key material and other secrets that must be strongly
24
+ protected.
25
+
26
+ This threshold secret sharing method is based on polynomial interpolation in
27
+ GF(256) and also provides a robust format for the storage and transmission
28
+ of shares.
29
+
30
+ The sharing format is Robust Threshold Secret Sharing (RTSS) as described
31
+ in the Internet-Draft. RTSS is a binary data format and a method for
32
+ ensuring that any secrets recovered are identical to the secret that was
33
+ originally shared.
34
+
35
+ This implementation supports RTSS digest types for `NONE`, `SHA1`, and
36
+ `SHA256`. `SHA256` is the recommended digest. In the RTSS scheme a digest of
37
+ the original secret is concatenated with the secret itself prior to the splitting
38
+ of the secret into shares. Later, this digest is compared with any secret recovered
39
+ by recombining shares. If the hash of the recovered secret does not match the
40
+ original hash stored in the shares the secret will not be returned. The verifier
41
+ hash for the secret is not available to shareholders prior to recombining shares.
42
+
43
+ The specification also addresses the optional implementation of a `MAGIC_NUMBER` and
44
+ advanced error correction schemes. These extras are not currently implemented.
45
+
46
+
47
+ ## Installation
48
+
49
+ Add this line to your application's `Gemfile`:
50
+
51
+ ```ruby
52
+ gem 'tss', '~> 0.1'
53
+ ```
54
+
55
+ And then execute:
56
+ ```sh
57
+ $ bundle
58
+ ```
59
+
60
+ Or install it yourself as:
61
+
62
+ ```sh
63
+ $ gem install tss
64
+ ```
65
+
66
+ ### Installation Security : Signed Ruby Gem
67
+
68
+ The TSS gem is cryptographically signed. To be sure the gem you install hasn’t
69
+ been tampered with you can install it using the following method:
70
+
71
+ Add my public key (if you haven’t already) as a trusted certificate
72
+
73
+ ```
74
+ # Caveat: Gem certificates are trusted globally, such that adding a
75
+ # cert.pem for one gem automatically trusts all gems signed by that cert.
76
+ gem cert --add <(curl -Ls https://raw.github.com/grempe/tss-rb/master/certs/gem-public_cert_grempe.pem)
77
+ ```
78
+
79
+ To install, it is possible to specify either `HighSecurity` or `MediumSecurity`
80
+ mode. Since the `tss` gem depends on one or more gems that are not cryptographically
81
+ signed you will likely need to use `MediumSecurity`. You should receive a warning
82
+ if any signed gem does not match its signature.
83
+
84
+ ```
85
+ # All dependent gems must be signed and verified.
86
+ gem install tss -P MediumSecurity
87
+ ```
88
+
89
+ ```
90
+ # All signed dependent gems must be verified.
91
+ gem install tss -P MediumSecurity
92
+ ```
93
+
94
+ ```
95
+ # Same as above, except Bundler only recognizes
96
+ # the long --trust-policy flag, not the short -P
97
+ bundle --trust-policy MediumSecurity
98
+ ```
99
+
100
+ You can [learn more about security and signed Ruby Gems](http://guides.rubygems.org/security/).
101
+
102
+ ### Installation Security : Signed Git Commits
103
+
104
+ Most, if not all, of the commits and tags to the repository for this code are
105
+ signed with my PGP/GPG code signing key. I have uploaded my code signing public
106
+ keys to GitHub and you can now verify those signatures with the GitHub UI.
107
+ See [this list of commits](https://github.com/grempe/tss-rb/commits/master)
108
+ and look for the `Verified` tag next to each commit. You can click on that tag
109
+ for additional information.
110
+
111
+ You can also clone the repository and verify the signatures locally using your
112
+ own GnuPG installation. You can find my certificates and read about how to conduct
113
+ this verification at [https://www.rempe.us/keys/](https://www.rempe.us/keys/).
114
+
115
+ ## TSS : Suggestions for Use
116
+
117
+ * Don't split large texts. Instead, split the much smaller encryption
118
+ keys that protect encrypted large texts. Supply the encrypted
119
+ files and the shares separately to recipients. Threshold secret sharing can be
120
+ very slow at splitting and recombining very large bodies of text, especially
121
+ when combined with a large number of shares. Every byte of the secret must
122
+ be processed `num_shares` times.
123
+
124
+ * Don't treat shares like encrypted data, but instead like the encryption keys
125
+ that unlock the data. Shares are keys, and need to be protected as such. There is
126
+ nothing to slow down an attacker if they have access to enough shares.
127
+
128
+ * If you send keys by email, or some other insecure channel, then your email
129
+ provider, or any entity with access to their data, now also has the keys to
130
+ your data. They just need to collect enough keys to meet the threshold.
131
+
132
+ * Use public key cryptography to encrypt secret shares with the public key of
133
+ each individual recipient. This can protect the share data from unwanted use while
134
+ in transit or at rest.
135
+
136
+ * Put careful thought into how you want to distribute shares. It often makes
137
+ sense to give individuals more than one share.
138
+
139
+ ## Command Line Interface
140
+
141
+ When you install the gem a simple `tss` command-line interface (CLI)
142
+ is also installed and should be available on your PATH.
143
+
144
+ The CLI is a simple interface for splitting and combining secrets. You can
145
+ see the options available with `tss help`, `tss help split`, or `tss help combine`.
146
+
147
+ ### CLI Secret Splitting
148
+
149
+ ```
150
+ $ tss help split
151
+ Usage:
152
+ tss split SECRET
153
+
154
+ Options:
155
+ -t, [--threshold=threshold] # # of shares, of total, required to reconstruct a secret
156
+ -n, [--num-shares=num_shares] # # of shares total that will be generated
157
+ -i, [--identifier=identifier] # A unique identifier string, 0-16 Bytes, [a-zA-Z0-9.-_]
158
+ -h, [--hash-alg=hash_alg] # A hash type for verification, NONE, SHA1, SHA256
159
+ -f, [--format=format] # Share output format, binary or human
160
+ # Default: human
161
+ -p, [--pad-blocksize=pad_blocksize] # Block size # secrets will be left-padded to, 0-255
162
+
163
+ Description:
164
+ `tss split` will generate a set of Threshold Secret Sharing shares from the SECRET provided. To protect your secret from being saved in
165
+ your shell history you will be prompted for the single-line secret.
166
+
167
+ Optional Params:
168
+
169
+ num_shares : The number of total shares that will be generated.
170
+
171
+ threshold : The threshold is the number of shares required to recreate a secret. This is always a subset of the total shares.
172
+
173
+ identifier : A unique identifier string that will be attached to each share. It can be 0-16 Bytes long and use the characters
174
+ [a-zA-Z0-9.-_]
175
+
176
+ hash_alg : One of NONE, SHA1, SHA256. The algorithm to use for a one-way hash of the secret that will be split along with the secret.
177
+
178
+ pad_blocksize : An Integer, 0-255, that represents a multiple to which the secret will be padded. For example if pad_blocksize is set to
179
+ 8, the secret 'abc' would be left-padded to '00000abc' (the padding char is not zero, that is just for illustration).
180
+
181
+ format : Whether to output the shares as a binary octet string (RTSS), or the same encoded as more human friendly Base 64 text with some
182
+ metadata prefixed.
183
+
184
+ Example using all options:
185
+
186
+ $ tss split -t 3 -n 6 -i abc123 -h SHA256 -p 8 -f human
187
+
188
+ Enter your secret:
189
+
190
+ secret > my secret
191
+
192
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEBQ-AQG3PuU4oT4qHOh2oJmu-vQwGE6O5hsGRBNtdAYauTIi7VoIdi5imWSrswDdRy
193
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADECM0OK5TSamH3nubH3FJ2EGZ4Yux4eQC-mvcYY85oOe6ae3kpvVXjuRUDU1m6sX20X
194
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEDb7yF4Vhr1JqNe2Nc8IXo98hmKAxsqC3c_Mn3r3t60NxQMC22ate51StDOM-BImch
195
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEEIXU0FajldnRtEQMLK-ZYMO2MRa0NmkBFfNAOx7olbgXLkVbP9txXMDsdokblVwke
196
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEFfYo7EcQUOpMH09Ggz_403rvy1r9_ckI_Pd_hm1tRxX8FfzEWyXMAoFCKTOfIKgMo
197
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEGDSmh74Ng8WTziMGZXAm5XcpFLqDl2oP4MH24XhYf33IIg1WsPIyMAznI0DJUeLpN
198
+
199
+ ```
200
+
201
+ **Example CLI Split Usage**
202
+
203
+ For security purposes you will be prompted for your secret and cannot enter it
204
+ on the command line.
205
+
206
+ ```
207
+ $ tss split -i abc
208
+ Enter your secret:
209
+ secret > abc
210
+ tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQB4zjuAvBL1P2AJciAHdicf6I2qxMkLGo2Hhr4dhI_v1CSKrE=
211
+ tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQCNAFhHSQd8nDgihYUrdM_IsMJqYZicLuk8jBS06kUJLZTU2g=
212
+ tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQDtlvspaxAmQJhYDTV8Ut9AM8dISVFPXIE-1A2EavU-hTBbHQ=
213
+ tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQE-NVr8ofyfwYVW9_2yauIT7t4Hmt9WeFNN_ADt7vpThYNeeU=
214
+ tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQFeo_mSg-vFHSUsf03lTPKbbdslshaFCjtPpBndbkpkLSfRvk=
215
+ ```
216
+
217
+ ### CLI Share Combining
218
+
219
+ **Example CLI Combine Usage**
220
+
221
+ For security purposes you will be prompted for your shares and cannot them
222
+ on the command line.
223
+
224
+ ```sh
225
+ tss combine
226
+ Enter shares, one per line, blank line or dot (.) to finish:
227
+ share> tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQB4zjuAvBL1P2AJciAHdicf6I2qxMkLGo2Hhr4dhI_v1CSKrE=
228
+ share> tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQCNAFhHSQd8nDgihYUrdM_IsMJqYZicLuk8jBS06kUJLZTU2g=
229
+ share> tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQDtlvspaxAmQJhYDTV8Ut9AM8dISVFPXIE-1A2EavU-hTBbHQ=
230
+ share>
231
+
232
+ Secret Recovered and Verified!
233
+
234
+ identifier : abc
235
+ threshold : 3
236
+ processing time (ms) : 0.48
237
+ secret :
238
+ **************************************************
239
+ abc
240
+ **************************************************
241
+ ```
242
+
243
+ ## Ruby : Splitting a Secret
244
+
245
+ The basic usage is as follows using the arguments described below.
246
+
247
+ ```ruby
248
+ shares = TSS.split(secret: secret,
249
+ threshold: threshold,
250
+ num_shares: num_shares,
251
+ identifier: identifier,
252
+ hash_alg: hash_alg,
253
+ pad_blocksize: pad_blocksize)
254
+ ```
255
+
256
+ ### Arguments
257
+
258
+ All arguments are passed as keys in a single Hash.
259
+
260
+ The `secret` (required) value must be provided as a String with either
261
+ a `UTF-8` or `US-ASCII` encoding. The Byte length must be `<= 65,534`. You can
262
+ test this beforehand with `'my string secret'.bytes.to_a.length`. Keep in mind
263
+ that this length also includes padding and the verification hash so your
264
+ secret may need to be shorter depending on the settings you choose.
265
+
266
+ Internally, the `secret` String will be converted to and processed as an Array
267
+ of Bytes. e.g. `'foo'.bytes.to_a`
268
+
269
+ The `num_shares` and `threshold` values are Integers representing the total
270
+ number of shares desired, and how many of those shares are required to
271
+ re-create a `secret`. Both arguments must be Integers with a value
272
+ between `1-255` inclusive. They can be Strings if directly coercible to Ints.
273
+ The `num_shares` value must be greater-than-or-equal-to the `threshold` value.
274
+ If you don't pass in these options they will be set to `threshold = 3` and
275
+ `num_shares = 5` by default.
276
+
277
+ The `identifier` is a `0-16` Byte String that will be embedded in
278
+ each output share and should uniquely identify a secret. All shares output
279
+ from the same secret splitting operation will have the same `identifier`. This
280
+ value can be retrieved easily from a share header and should be assumed to be
281
+ known to shareholders. Nothing that leaks information about the secret should
282
+ be used as an `identifier`. If an `identifier` is not set, it will default
283
+ to the output of `SecureRandom.hex(8)` which is 8 random hex bytes and
284
+ 16 characters long.
285
+
286
+ The `hash_alg` is a String that represents which cryptographic one-way
287
+ hash function should be embedded in shares. The hash is used to verify
288
+ that the re-combined secret is a match for the original at creation time.
289
+ The value of the hash is not available to shareholders until a secret is
290
+ successfully re-combined. The hash is calculated from the original secret
291
+ and then combined with it prior to secret splitting. This means that the hash
292
+ is protected the same way as the secret. The algorithm used is
293
+ `secret || hash(secret)`. You can use one of `NONE`, `SHA1`, or `SHA256`.
294
+
295
+ The `format` arg takes a String Enum with either `'binary'` or `'human'` values.
296
+ This instructs the output of a split to either provide an array of binary octet
297
+ strings (standard RTSS format for interoperability), or a human friendly
298
+ URL Safe Base 64 encoded version of that same binary output. The `human` format
299
+ can be easily shared in a tweet, email, or even a URL. The `human` format is
300
+ prefixed with `tss-VERSION-IDENTIFIER-THRESHOLD-` to make it easier to visually
301
+ compare shares and see if they have matching identifiers and if you have
302
+ enough shares to reach the threshold. Note, this prefix is not parsed
303
+ or used by the `tss` combiner code at all. It is only for user convenience.
304
+
305
+ The `pad_blocksize` arg takes an Integer between 0..255 inclusive. Your secret
306
+ **MUST NOT** *begin* with this character (which was chosen to make less likely).
307
+ The padding character used is `"\u001F"` `Unit Separator, decimal 31`.
308
+
309
+ Padding is applied to the nearest multiple of the number of bytes specified.
310
+ `pad_blocksize` defaults to no padding (0). For example:
311
+
312
+ ```ruby
313
+ padding_blocksize: 8
314
+ (padded with zeros for illustration purposes)
315
+
316
+ # a single char, padded up to 8
317
+ 'a' -> "0000000a"
318
+
319
+ # 8 chars, no padding needed to get to 8
320
+ 'aaaaaaaa' -> "aaaaaaaa"
321
+
322
+ # 9 chars, bumps blocksize up to 16 and pads
323
+ 'aaaaaaaaa' -> "0000000aaaaaaaaa"
324
+ ```
325
+
326
+ Since TSS share data is essentially the same size as the original secret
327
+ (with a known size header), padding smaller secrets may help mask the size
328
+ of the secret itself from an attacker. Padding is not part of the RTSS spec
329
+ so other TSS clients won't strip off the padding and may fail when recombining
330
+ shares. If you need this level of interoperability you should probably skip
331
+ the `pad_blocksize` padding and just pad the secret yourself prior to splitting
332
+ it. You need to pad using a character other than `"\u001F"`.
333
+
334
+ If you want to do padding this way, there is a utility method you can use
335
+ to do that. This is the same method used internally.
336
+
337
+ ```ruby
338
+ # Util.left_pad(byte_multiple, input_string, pad_char = "\u001F")
339
+
340
+ > Util.left_pad(16, 'abc', "0")
341
+ => "0000000000000abc"
342
+ ```
343
+
344
+ ### Example Usage
345
+
346
+ ```ruby
347
+ secret = 'foo bar baz'
348
+ threshold = 3
349
+ num_shares = 5
350
+ identifier = SecureRandom.hex(8)
351
+ hash_alg = 'SHA256'
352
+
353
+ s = TSS.split(secret: secret, threshold: threshold, num_shares: num_shares, identifier: identifier, hash_alg: 'SHA256', pad_blocksize: 16)
354
+
355
+ => ["c70963b2e20fccfd\x02\x03\x001\x01\x1Fg4\xDC\xAA\x96\x9D3\xCB\xFB\xF7\xB0\x91}\xCA\xB7\x0E\xB0\xF3.}O\xD0&Z\x11\xB0\xAB\xF48f#*\xBA\xB7)l\x05\xAF4\xFA\x95\x9C\xF2\x8E\xA6\xB9=",
356
+ "c70963b2e20fccfd\x02\x03\x001\x02Y|\x1F\x1Co\x8BW\f^\xFE\xA5\x92G\xA4\xD0K\xC6@G\xDC\x02\xBF\xF1\xAE\xE7\vP\xF1*\x9C\xA5$\edM#\xB0\xEBy\a}\xA18\rBZ\x8A\xEE",
357
+ "c70963b2e20fccfd\x02\x03\x001\x03Y\x044\xDF\xDA{\xA5P\xB5g3P\xF6\xBB{\x86\x13#\xAC3\xBB\x92\x8F`\xCF\xEE\xF1Sz{\x10\x03\xB9\xAFZ71>(=\xF2HI\xA8\x16*\xC1\x04",
358
+ "c70963b2e20fccfd\x02\x03\x001\x04\x90\xA3\\W\xFB\xFF.\xE8&\xA3\x13N\x968\xC5\xEEg\xA1\xD8\xB6\xD9\xE9\xAAMz\xA9\xF3H\e7#\xE7\xA8\r@\xD9\\\xB8\x7F\xF3Q\x8D\x80\xCF1~\x97P",
359
+ "c70963b2e20fccfd\x02\x03\x001\x05\x90\xDBw\x94N\x0F\xDC\xB4\xCD:\x85\x8C''n#\xB2\xC23Y`\xC4\xD4\x83RLR\xEAK\xD0\x96\xC0\n\xC6W\xCD\xDDm.\xC9\xDEd\xF1je\x0E\xDC\xBA"]
360
+
361
+ secret = TSS.combine(shares: s)
362
+ => {:identifier=>"c70963b2e20fccfd",
363
+ :num_shares_provided=>5,
364
+ :num_shares_used=>3,
365
+ :processing_started_at=>"2016-04-10T00:58:04Z",
366
+ :processing_finished_at=>"2016-04-10T00:58:04Z",
367
+ :processing_time_ms=>0.37,
368
+ :secret=>"foo bar baz",
369
+ :shares_select_by=>"first",
370
+ :combinations=>nil,
371
+ :threshold=>3}
372
+ ```
373
+
374
+ ## Ruby : Combining Shares to Recreate a Secret
375
+
376
+ The basic usage is:
377
+
378
+ ```ruby
379
+ secret = TSS.combine(shares: shares)
380
+ ```
381
+
382
+ ### Arguments
383
+
384
+ All arguments are passed as keys in a single Hash. The return value is either
385
+ a Hash (with the `:secret` key being most important and other metadata provided
386
+ for informational purposes), or an `TSS::Error` Exception if the secret could
387
+ not be created and verified with its hash.
388
+
389
+ `shares:` (required) : Must be provided as an Array of encoded Share Byte Strings.
390
+ You must provide at least `threshold` shares as specified when the secret was
391
+ split. Providing too few shares will result in a `TSS::ArgumentError` exception
392
+ being raised. There are a large number of verifications that are performed on
393
+ shares provided to make sure they are valid and consistent with each other. An
394
+ Exception will be raised if any of these tests fail.
395
+
396
+ `select_by:` : If the number of shares provided as input to the secret
397
+ reconstruction operation is greater than the threshold M, then M
398
+ of those shares are selected for use in the operation. The method
399
+ used to select the shares can be chosen with the `select_by:` argument
400
+ which takes the following values as options:
401
+
402
+ `select_by: 'first'` : If X shares are required by the threshold and more than X
403
+ shares are provided, then the first X shares in the Array of shares provided
404
+ will be used. All others will be discarded and the operation will fail if
405
+ those selected shares cannot recreate the secret.
406
+
407
+ `select_by: 'sample'` : If X shares are required by the threshold and more than X
408
+ shares are provided, then X shares will be randomly selected from the Array
409
+ of shares provided. All others will be discarded and the operation will
410
+ fail if those selected shares cannot recreate the secret.
411
+
412
+ `select_by: 'combinations'` : If X shares are required, and more than X shares are
413
+ provided, then all possible combinations of the threshold number of shares
414
+ will be tried to see if the secret can be recreated.
415
+
416
+ **Warning**
417
+
418
+ This `combinations` flexibility comes with a cost. All combinations of
419
+ `threshold` shares must be generated before processing. Due to the math
420
+ associated with combinations it is possible that the system would try to
421
+ generate a number of combinations that could never be generated or processed
422
+ in many times the life of the Universe. This option can only be used if the
423
+ possible combinations for the number of shares and the threshold needed to
424
+ reconstruct a secret result in a number of combinations that is small enough
425
+ to have a chance at being processed. If the number of combinations will be too
426
+ large then the an Exception will be raised before processing has started.
427
+
428
+ If the combine operation does not result in a secret being successfully
429
+ extracted, then a `TSS::Error` exception will be raised.
430
+
431
+ ### Exception Handling
432
+
433
+ Initial validation of options is done when the `TSS.split` or `TSS.combine`
434
+ methods are called. If the arguments passed are of the wrong Type or value
435
+ a `Dry::Types::ConstraintError` Exception will be raised.
436
+
437
+ The splitting and combining operations may also raise `TSS::ArgumentError`
438
+ or `TSS::Error` exceptions as they are run.
439
+
440
+ All Exception messages should include hints as to what went wrong in the
441
+ `ex.messages` attribute.
442
+
443
+ ## Share Data Formats
444
+
445
+ ### RTSS Binary
446
+
447
+ TSS provides shares in a binary data format with the following fields:
448
+
449
+ `Identifier`. This field contains 16 octets. It identifies the secret
450
+ with which a share is associated. All of the shares associated
451
+ with a particular secret MUST use the same value Identifier. When
452
+ a secret is reconstructed, the Identifier fields of each of the
453
+ shares used as input MUST have the same value. The value of the
454
+ Identifier should be chosen so that it is unique, but the details
455
+ on how it is chosen are left as an exercise for the reader. The
456
+ characters `a-zA-Z0-9.-_` are allowed in the identifier.
457
+
458
+ `Hash Algorithm Identifier`. This field contains a single octet that
459
+ indicates the hash function used in the RTSS processing, if any.
460
+ A value of `0` indicates that no hash algorithm was used, no hash
461
+ was appended to the secret, and no RTSS check should be performed
462
+ after the reconstruction of the secret.
463
+
464
+ `Threshold`. This field contains a single octet that indicates the
465
+ number of shares required to reconstruct the secret. This field
466
+ MUST be checked during the reconstruction process, and that
467
+ process MUST halt and return an error if the number of shares
468
+ available is fewer than the value indicated in this field.
469
+
470
+ `Share Length`. This field is two octets long. It contains the number
471
+ of octets in the Share Data field, represented as an unsigned
472
+ integer in network byte order.
473
+
474
+ `Share Data`. This field has a length that is a variable number of
475
+ octets. It contains the actual share data.
476
+
477
+ ```
478
+ 0 1 2 3
479
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
480
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
481
+ | |
482
+ | Identifier |
483
+ | |
484
+ | |
485
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
486
+ | Hash Alg. Id. | Threshold | Share Length |
487
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
488
+ : :
489
+ : Share Data :
490
+ : :
491
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
492
+ ```
493
+
494
+ This code has been tested for binary compatibility with the
495
+ [seb-m/tss](https://github.com/seb-m/tss) Python implementation of TSS. There
496
+ are test cases to ensure it remains compatible.
497
+
498
+ ### RTSS Human Friendly Wrapper
499
+
500
+ To make `tss` more friendly to use when sending shares to others an enhanced
501
+ version of the RTSS binary data format is provided that is more human friendly.
502
+
503
+ Shares formatted this way can easily be shared via any communication channel.
504
+
505
+ The `human` data format is simply the same RTSS binary data, URL Safe Base64
506
+ encoded, and prefixed with a String thet contains the following tilde `~`
507
+ separated elements. The `~` is used to ensure the share remains URL Safe.
508
+
509
+ ```text
510
+ tss~VERSION~IDENTIFIER~THRESHOLD~BASE64_ENCODED_BINARY_SHARE
511
+ ```
512
+
513
+ A typical share with this format looks like:
514
+
515
+ ```text
516
+ tss~v1~abc~3~YWJjAAAAAAAAAAAAAAAAAAIDACQB10mUbJPQZ94WpgKC2kKivfnSpCHTMr6BajbwzqOrvyMCpH0=
517
+ ```
518
+
519
+ ## Performance
520
+
521
+ The amount of time it takes to split or combine secrets grows significantly as
522
+ the size of the secret and the total `num_shares` and `threshold` increase.
523
+ Splitting a secret with the maximum size of `2**16 - 2` (65,534) Bytes and
524
+ the maximum `255` shares may take an unreasonably long time to run. Splitting
525
+ and Combining involves at least `num_shares**secret_bytes` operations so
526
+ larger values can quickly result in huge processing time. If you need to
527
+ split large secrets into a large number of shares you should consider
528
+ running those operations in a background worker process or thread for
529
+ best performance.
530
+
531
+ A reasonable set of values seems to be what I'll call the 'rule of 64'. If you
532
+ keep the `secret <= 64 Bytes`, the `threshold <= 64`, and the `num_shares <= 64`
533
+ you can do a round-trip split and combine operation in `~250ms` on a modern
534
+ laptop. These should be very reasonable and secure max values for most use cases.
535
+
536
+ There are some simple benchmark tests to exercise things with `rake bench`.
537
+
538
+ ## Development
539
+
540
+ After checking out the repo, run `bin/setup` to install dependencies. Then,
541
+ run `rake test` to run the tests. You can also run `bin/console` for an
542
+ interactive prompt that will allow you to experiment.
543
+
544
+ To install this gem onto your local machine, run `bundle exec rake install`. To
545
+ release a new version, update the version number in `version.rb`, and then
546
+ run `bundle exec rake release`, which will create a git tag for the version,
547
+ push git commits and tags, and push the `.gem` file
548
+ to [rubygems.org](https://rubygems.org).
549
+
550
+ ### Contributing
551
+
552
+ Bug reports and pull requests are welcome on GitHub
553
+ at [https://github.com/grempe/tss-rb](https://github.com/grempe/tss-rb). This
554
+ project is intended to be a safe, welcoming space for collaboration, and
555
+ contributors are expected to adhere to the
556
+ [Contributor Covenant](http://contributor-covenant.org) code of conduct.
557
+
558
+ ## Legal
559
+
560
+ ### Copyright
561
+
562
+ (c) 2016 Glenn Rempe <[glenn@rempe.us](mailto:glenn@rempe.us)> ([https://www.rempe.us/](https://www.rempe.us/))
563
+
564
+ ### License
565
+
566
+ The gem is available as open source under the terms of
567
+ the [MIT License](http://opensource.org/licenses/MIT).
568
+
569
+ ### Warranty
570
+
571
+ Unless required by applicable law or agreed to in writing,
572
+ software distributed under the License is distributed on an
573
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
574
+ either express or implied. See the LICENSE.txt file for the
575
+ specific language governing permissions and limitations under
576
+ the License.
577
+
578
+ ## Thank You!
579
+
580
+ This code is an implementation of the Threshold Secret Sharing, as specified in
581
+ the Network Working Group Internet-Draft submitted by D. McGrew
582
+ ([draft-mcgrew-tss-03.txt](http://tools.ietf.org/html/draft-mcgrew-tss-03)).
583
+ This code would not have been possible without this very well designed and
584
+ documented specification. Many examples of the relevant text from the specification
585
+ have been used as comments to annotate this source code.
586
+
587
+ Great respect to Sébastien Martini ([@seb-m](https://github.com/seb-m)) for
588
+ his [seb-m/tss](https://github.com/seb-m/tss) Python implementation of TSS.
589
+ It was invaluable as a real-world reference implementation of the
590
+ TSS Internet-Draft.