tss 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +4 -0
- data/.codeclimate.yml +30 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +1156 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +590 -0
- data/Rakefile +23 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/tss +7 -0
- data/certs/gem-public_cert_grempe.pem +21 -0
- data/docs/tss-ietf-draft/draft-mcgrew-tss-03.html +1417 -0
- data/docs/tss-ietf-draft/draft-mcgrew-tss-03.txt +1456 -0
- data/lib/tss.rb +14 -0
- data/lib/tss/blank.rb +142 -0
- data/lib/tss/cli.rb +107 -0
- data/lib/tss/combiner.rb +296 -0
- data/lib/tss/errors.rb +4 -0
- data/lib/tss/hasher.rb +55 -0
- data/lib/tss/splitter.rb +190 -0
- data/lib/tss/tss.rb +15 -0
- data/lib/tss/types.rb +4 -0
- data/lib/tss/util.rb +266 -0
- data/lib/tss/version.rb +3 -0
- data/tss.gemspec +58 -0
- metadata +223 -0
- metadata.gz.sig +0 -0
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.4
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -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
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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.
|