tss 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 66cc2d0e14e513c4d37219532ddb7fcbefe4da87
4
- data.tar.gz: 5e2498426afb7fbd733b666771876a491f2f9d14
3
+ metadata.gz: 3597bdbe0559f746e7f6b44c9e15b493cf48d92e
4
+ data.tar.gz: b049be29b2c0f1a1360e3114133f0524ad361562
5
5
  SHA512:
6
- metadata.gz: 43f7ab59473f94feeb3fd3ac710680b082059da04fda2ca0361be4dcc57c1c30a3e872700e0d742f254942dbb7c604d12c1fe6f5021f44ae791d74f8aa5a7560
7
- data.tar.gz: c42ec3ed1b0a047ee608d7f34ab26279f550af83c90f80ee28e838d97674518631109e6aae15b284413b5d7fd332370c8ccd1b2377eb087aec7250e1890fbb29
6
+ metadata.gz: 37dc3d8473236a57165d3e12c2accd5e66b9dd769ad80c6e401f73e261227ac3b3db93749f3188878a77b65512fc3d011384f4e07f37f177e13490a41ab5bd28
7
+ data.tar.gz: 3a6842d643b9ccb6121bcef80d15453ae26a5ea3fd8db926d9d1a0384008e7089dd5ed232ecc2bc4df74b18c5fc6e2b460a4b9fca85f1421ad27a3c046d555d3
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -743,7 +743,7 @@ Style/LineEndConcatenation:
743
743
  line end.
744
744
  Enabled: false
745
745
 
746
- Style/MethodCallParentheses:
746
+ Style/MethodCallWithoutArgsParentheses:
747
747
  Description: 'Do not use parentheses for method calls with no arguments.'
748
748
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens'
749
749
  Enabled: false
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.4.0
@@ -2,5 +2,6 @@ language: ruby
2
2
  rvm:
3
3
  - 2.2.5
4
4
  - 2.3.1
5
+ - 2.4.0
5
6
  - jruby-9.1.5.0
6
7
  before_install: gem install bundler -v 1.13.2
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v0.5.0 (1/28/2017)
4
+
5
+ * Breaking refactor to use automatic PKCS#7 padding on secrets w/ 16 byte block size.
6
+ * Update Copyright year
7
+ * Ruby 2.4.0 testing
8
+ * Update version of sysrandom gem
9
+ * Fixed minitest spec warning
10
+
3
11
  ## v0.4.2 (10/12/2016)
4
12
 
5
13
  * Sign the gem with a new cert that expires in 10 years.
data/README.md CHANGED
@@ -74,29 +74,29 @@ secret unicode characters ½ ♥ 💩
74
74
 
75
75
  ```ruby
76
76
  $ bundle exec bin/console
77
- [1] pry(main)> require 'tss'
77
+ > require 'tss'
78
78
  => false
79
- [2] pry(main)> shares = TSS.split(secret: 'my deep dark secret')
80
- => ["tss~v1~72e84cd688bf24fc~3~NzJlODRjZDY4OGJmMjRmYwIDADQBVLUhLlOvz_VWjEge2gH7PtqTsnSCpbfmMSFX98XUp0BmQAeeOTGDL0GVeGATWjbBZvOm",
81
- "tss~v1~72e84cd688bf24fc~3~NzJlODRjZDY4OGJmMjRmYwIDADQCmlZ_CZSBPk6m2-0WMEmcdzCsB8lH1oDalELd9VhlFPqby08kjm9tZBtONqXucgcnuFlQ",
82
- "tss~v1~72e84cd688bf24fc~3~NzJlODRjZDY4OGJmMjRmYwIDADQDo5p-Q6JLgZuUNtdjyjsCKphawUx8bNhW0FYjdk7V_4RRnZpzsCzi00hLb4igNYYraY5Z",
83
- "tss~v1~72e84cd688bf24fc~3~NzJlODRjZDY4OGJmMjRmYwIDADQEuqHxd94BQK1V-db70VLZxdIULyVIOGDrE7w7K3SVnk0UaEMf9xJFaYpKrXical7nR9PT",
84
- "tss~v1~72e84cd688bf24fc~3~NzJlODRjZDY4OGJmMjRmYwIDADQFg23wPejL_3hnFOyOKyBHmHri6aBzgjhnV6jFqGIldTPePpZIyVHK3tlP9FXSLd_rlgTa"]
85
- [3] pry(main)> secret = TSS.combine(shares: shares)
79
+ > shares = TSS.split(secret: 'my deep dark secret')
80
+ => ["tss~v1~506168fade769236~3~NTA2MTY4ZmFkZTc2OTIzNgIDAEEBIM39jPGXFz4zKCObWgp2zQmCuUx92-VUf48FWQFqnLF3bw6VtVjYK7JRfESZIREdzhWcQuTQVuGKazxWRK27Tg==",
81
+ "tss~v1~506168fade769236~3~NTA2MTY4ZmFkZTc2OTIzNgIDAEEC1E8YyDhp5NeZvF6vHeUT6HoiU0AgoF69jyjNRbtcIi1YoymJTau1rJP-3nQXOofaB2LgnAhJpbB8vrID9WTKgQ==",
82
+ "tss~v1~506168fade769236~3~NTA2MTY4ZmFkZTc2OTIzNgIDAEEDmfvFIKybg8nO9Q9fZ5wARgHFngFQdrbk_arFEbc7s5HedTjzkoZYLlV8xnywt4AVAHAO0nSLY3C7iJPifH5VYA==",
83
+ "tss~v1~506168fade769236~3~NTA2MTY4ZmFkZTc2OTIzNgIDAEEEiBoCoHycMNTS5yQEU8_Sc7eVlIZOJP1-ka_7MPTlC1p2DyNdGvQxy3pBFZyH8WXAY9ICBxiZRDCJisFrebviAg==",
84
+ "tss~v1~506168fade769236~3~NTA2MTY4ZmFkZTc2OTIzNgIDAEEFxa7fSOhuV8qFrnX0KbbB3cxyWcc-8hUn4y3zZPiCmubw2TInxdncSbzDDZQgfGIPZMDsSWRbgvBOvOCK8KF94w=="]
85
+ > secret = TSS.combine(shares: shares)
86
86
  => {:hash=>"f1b91fef6a7535a974d3644c3eac16d2c907720c981290214d5d1db7cdb724af",
87
87
  :hash_alg=>"SHA256",
88
- :identifier=>"72e84cd688bf24fc",
89
- :process_time=>0.58,
88
+ :identifier=>"506168fade769236",
89
+ :process_time=>0.94,
90
90
  :secret=>"my deep dark secret",
91
91
  :threshold=>3}
92
- [4] pry(main)> puts secret[:secret]
92
+ > puts secret[:secret]
93
93
  my deep dark secret
94
94
  => nil
95
95
  ```
96
96
 
97
97
  ## Is it any good?
98
98
 
99
- While this implementation has not had a formal security review, the cryptographic
99
+ While this implementation has not yet had a formal security review, the cryptographic
100
100
  underpinnings were carefully specified in an IETF draft document authored by a
101
101
  noted cryptographer. I have reached out to individuals respected in the field
102
102
  for their work in implementing cryptographic solutions to help review this code.
@@ -128,13 +128,14 @@ nothing to slow down an attacker if they have access to enough shares.
128
128
 
129
129
  * If you send keys by email, or some other insecure channel, then your email
130
130
  provider, or any entity with access to their data, now also has the keys to
131
- your data. They just need to collect enough keys to meet the threshold.
131
+ your data. They just need to collect enough keys to meet the threshold. Sending
132
+ enough shares to recreate a secret through any single provider offers no
133
+ more security than sending the key itself.
132
134
 
133
135
  * Use public key cryptography to encrypt secret shares with the public key of
134
136
  each individual recipient. This can protect the share data from unwanted use while
135
- in transit or at rest. Excellent choices might be
136
- [RbNaCl](https://github.com/cryptosphere/rbnacl)
137
- or [TweetNaCl.js](https://github.com/dchest/tweetnacl-js).
137
+ in transit or at rest. OpenPGP would be one such tool for encrypting shares to
138
+ send safely.
138
139
 
139
140
  * Put careful thought into how you want to distribute shares. It often makes
140
141
  sense to give individuals more than one share.
@@ -144,15 +145,10 @@ sense to give individuals more than one share.
144
145
  There is pretty extensive inline documentation. You can view the latest
145
146
  auto-generated docs at [http://www.rubydoc.info/gems/tss](http://www.rubydoc.info/gems/tss)
146
147
 
147
- ## Supported Platforms
148
+ ## Supported Ruby Versions
148
149
 
149
- TSS is continuously integration tested on the following Ruby VMs:
150
-
151
- * MRI 2.2.5
152
- * MRI 2.3.1
153
- * jruby-9.0.5.0
154
-
155
- It may work on others as well.
150
+ TSS is continuously integration tested on a number of Ruby versions. See the file
151
+ `.travis.yml` in the root of this repository for the currently tested versions.
156
152
 
157
153
  ## Installation
158
154
 
@@ -261,12 +257,14 @@ cautioned that storing the secret on a filesystem may expose you to certain
261
257
  attacks since it can be hard to fully erase files once written.
262
258
 
263
259
  ```text
260
+ $ cat /tmp/secret.txt
261
+ my secret
264
262
  $ tss split -I /tmp/secret.txt
265
- tss~v1~3f784d9d04dff796~3~M2Y3ODRkOWQwNGRmZjc5NgIDACsBvAVAo1dH3QrsYl0S30j2VVTu6dZLRe9EEk6zLtPTwZNYk-t1e4SAA41D
266
- tss~v1~3f784d9d04dff796~3~M2Y3ODRkOWQwNGRmZjc5NgIDACsCMfy6YlI-CQrd-l93Xtw8YqOWSP5ToolKTaZyO2fPYzceaaVmB30ycdVr
267
- tss~v1~3f784d9d04dff796~3~M2Y3ODRkOWQwNGRmZjc5NgIDACsD4IDasmAapmVFkuYPMvuavCtXiJgPZsaBHglGm4FU_U2rGZzfapRk-ZNN
268
- tss~v1~3f784d9d04dff796~3~M2Y3ODRkOWQwNGRmZjc5NgIDACsEbId8z1yNfEkDcezJpstkYenGLhJCMRglx2c0arPDS-YuXyiiNQQ-jYlq
269
- tss~v1~3f784d9d04dff796~3~M2Y3ODRkOWQwNGRmZjc5NgIDACsFvfscH26p0yabGVWxyuzCv2EH7nQe9VfulMgAylVY1ZybLxEbWO1oBc9M
263
+ tss~v1~8b66eb89ee25a46c~3~OGI2NmViODllZTI1YTQ2YwIDACsBqgQvwFhKboLMAmYwF5_CvaUwM8pmqUipbMRzakdbP53WJa-E6tf2nl2M
264
+ tss~v1~8b66eb89ee25a46c~3~OGI2NmViODllZTI1YTQ2YwIDACsCk1EU-HwvC6pjvQ5wDas221Qs4A7TtJNpi-Su8hxOqrsCSQ8t11aiQUpm
265
+ tss~v1~8b66eb89ee25a46c~3~OGI2NmViODllZTI1YTQ2YwIDACsDVCwbS0EGF03btYwqqVuk7S0z-nSinHtPpsFaFm5dys85j3JlK-yCVNyP
266
+ tss~v1~8b66eb89ee25a46c~3~OGI2NmViODllZTI1YTQ2YwIDACsEPUzgi8CDn4ix1dLQTQDj2yTdeeQcZAvGuMyQ_H7EepN6WO_KB1DuqTxm
267
+ tss~v1~8b66eb89ee25a46c~3~OGI2NmViODllZTI1YTQ2YwIDACsF-jHvOP2qg28J3VCK6fBx7V3CY55tTOPglelkGAzXGudBnpKC--rOvKqP
270
268
  ```
271
269
 
272
270
  **Example : `interactive`**
@@ -282,11 +280,11 @@ Enter your secret, enter a dot (.) on a line by itself to finish :
282
280
  secret > the vault password is:
283
281
  secret > V0ulT!
284
282
  secret > .
285
- tss~v1~546604c0b5e9138b~3~NTQ2NjA0YzBiNWU5MTM4YgIDAD8BK-9qlyxRFFQypFLwzM_tLWOv-NRziwnc6blqFP8eh8wLro4qiQwBKfI0KuuvM0u1C6th1vxsyW8vyolXQ-4=
286
- tss~v1~546604c0b5e9138b~3~NTQ2NjA0YzBiNWU5MTM4YgIDAD8CEjeK01kAwE1wb7-wvLLlHoe6FGzhl7Hg9TCz_Npw7u0b31OpijFIKdxELfbnhvius7vGqn6KqQKqq69s4Co=
287
- tss~v1~546604c0b5e9138b~3~NTQ2NjA0YzBiNWU5MTM4YgIDAD8DTbCFZAMwoXU2650hAw5_XJZxzNHhJrJqLPy1vARk8APzWkgP7K79AtbVn1C49HpBSP2OL-BLumaZZWbU7YI=
288
- tss~v1~546604c0b5e9138b~3~NTQ2NjA0YzBiNWU5MTM4YgIDAD8E9eVJ83Knw3Fq1e9VaFSKdPKUAugyBqXzCsqov6etQIfEYe8Vt-ZVu3Ax7L1MWBNt-AQLN7YtczAG4QZ_7wI=
289
- tss~v1~546604c0b5e9138b~3~NTQ2NjA0YzBiNWU5MTM4YgIDAD8FqmJGRCiXokksUc3E1-gQNuNf2lUyt6Z50wau_3m5Xmks5PSz0XngkHqgXhsTKpGCA0JDsijsYFQ1L8_H4qo=
283
+ tss~v1~1f8532a9d185efc4~3~MWY4NTMyYTlkMTg1ZWZjNAIDAD8BJkMvkHUGPQk1HQr_jVj9JkhavJVkdeFDEtRAf8O6vjksCjMMjiyuUL0bOD7vDnmdrzHK8QVymXU5vnjAREs=
284
+ tss~v1~1f8532a9d185efc4~3~MWY4NTMyYTlkMTg1ZWZjNAIDAD8CJpiKCEtSfmugr_gbvEIBs2M2sZfHxuZsBuPpB35OGeyWmGZCyJCNu0W2z3abkV1Q8uTPvT75YYRpw6BcdvQ=
285
+ tss~v1~1f8532a9d185efc4~3~MWY4NTMyYTlkMTg1ZWZjNAIDAD8DdLPAuEg1Ng7hkoKFQmmL-lkILWvQiQ15JELFLJz-PvdZucDCqS-X6QAIbwWE3u2XrTgsH1kmIvpMeZhzfPk=
286
+ tss~v1~1f8532a9d185efc4~3~MWY4NTMyYTlkMTg1ZWZjNAIDAD8EJT1XJ-DeOXuE3JD6VpZk79945_peSk1ij1Mk5ql-l2jkv85yGBbyH0VIBmJp2WsEMmESZqTsQ9kKB-0Ljq8=
287
+ tss~v1~1f8532a9d185efc4~3~MWY4NTMyYTlkMTg1ZWZjNAIDAD8FdxYdl-O5cR7F4epkqL3upuVGewZJBaZ3rfIIzUvOsHMrnmjyeanoTQD2phF2ltvDbb3xxMMzAKcvvdUkhKI=
290
288
  ```
291
289
 
292
290
  ### CLI Share Combining
@@ -339,29 +337,29 @@ a secret
339
337
  ```text
340
338
  $ cat /tmp/shares.txt
341
339
  # THRESHOLD SECRET SHARING SHARES
342
- # 2016-04-19T23:57:17Z
340
+ # 2017-01-29T02:01:00Z
343
341
  # https://github.com/grempe/tss-rb
344
342
 
345
343
 
346
- tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoBRjAH7wA0ncxJr3GliBqKxedf3bGaQH6AscuLqxtrKxxtxuC7A7a5Sqk=
347
- tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoClWVu75w-XHhoUgkbV5XaJ11stZCB5Z8HTArpv4QsIYDBpNO9DHQTbQ4=
348
- tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoDsnUaZf94pMArKZL7jmavzk9Qa6ZAvOB8e8nEAt0nyS6ga0XBucQOyGc=
349
- tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoETCWwSGLHcutzGCLb1yOkH7i6MF7j2b0wr0ZsUh6LuBmx6FkN2MbTkTc=
350
- tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoFazXEwgGBilMwY7k7DtDR9qqG7mgigMJLmIVB70eAULfQJ89xbXbONF4=
344
+ tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoBbga2N__A9QOleLhC5R5b7stJ26iMPNpQ95YpmK-tWIrd-CYcrXGmcys=
345
+ tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoCGXsZkLOG7uDOVN1Zn46pqftX4noA8LfXugg14KBfSggYP9fw4Ce10YA=
346
+ tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoDFl3cwi80fpdh-I9eK3kNa8V9OlXX1Wx8y5a6bk2S0TDJzocr-1C3TWs=
347
+ tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoEEspmyzmbnrSr7v41FFlHVTqQ6dx4aho8q5T1CBHQbNimdCv3gVXS50I=
348
+ tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoFHeyjmaUpDsMEQqwyoK7jlwS6MfOvT8GX2gp6hvwd9-B3hXssmiLQe6k=
351
349
 
352
350
  $ tss combine
353
351
  Enter shares, one per line, and a dot (.) on a line by itself to finish :
354
- share> tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoBRjAH7wA0ncxJr3GliBqKxedf3bGaQH6AscuLqxtrKxxtxuC7A7a5Sqk=
355
- share> tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoClWVu75w-XHhoUgkbV5XaJ11stZCB5Z8HTArpv4QsIYDBpNO9DHQTbQ4=
356
- share> tss~v1~ae2983e30e0471fe~3~YWUyOTgzZTMwZTA0NzFmZQIDACoDsnUaZf94pMArKZL7jmavzk9Qa6ZAvOB8e8nEAt0nyS6ga0XBucQOyGc=
352
+ share> tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoBbga2N__A9QOleLhC5R5b7stJ26iMPNpQ95YpmK-tWIrd-CYcrXGmcys=
353
+ share> tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoCGXsZkLOG7uDOVN1Zn46pqftX4noA8LfXugg14KBfSggYP9fw4Ce10YA=
354
+ share> tss~v1~b9f7f87bc83fd89b~3~YjlmN2Y4N2JjODNmZDg5YgIDACoDFl3cwi80fpdh-I9eK3kNa8V9OlXX1Wx8y5a6bk2S0TDJzocr-1C3TWs=
357
355
  share> .
358
356
 
359
357
  RECOVERED SECRET METADATA
360
358
  *************************
361
359
  hash : d4ea4551e9ff2cf56303875b1901fb8608a6164260c3b20c0976c7b606a4efc0
362
360
  hash_alg : SHA256
363
- identifier : ae2983e30e0471fe
364
- process_time : 0.7ms
361
+ identifier : b9f7f87bc83fd89b
362
+ process_time : 1.22ms
365
363
  threshold : 3
366
364
  secret :
367
365
  a secret
@@ -373,11 +371,11 @@ The basic usage is as follows using the arguments described below.
373
371
 
374
372
  ```ruby
375
373
  shares = TSS.split(secret: secret,
376
- threshold: threshold,
377
- num_shares: num_shares,
378
- identifier: identifier,
379
- hash_alg: hash_alg,
380
- pad_blocksize: pad_blocksize)
374
+ threshold: threshold,
375
+ num_shares: num_shares,
376
+ identifier: identifier,
377
+ hash_alg: hash_alg,
378
+ padding: true)
381
379
  ```
382
380
 
383
381
  ### Arguments
@@ -385,7 +383,7 @@ shares = TSS.split(secret: secret,
385
383
  All arguments are passed as keys in a single Hash.
386
384
 
387
385
  The `secret` (required) value must be provided as a String with either
388
- a `UTF-8` or `US-ASCII` encoding. The Byte length must be `<= 65,534`. You can
386
+ a `UTF-8` or `US-ASCII` encoding. The Byte length must be `<= 65,486`. You can
389
387
  test this beforehand with `'my string secret'.bytes.to_a.length`. Keep in mind
390
388
  that this length also includes padding and the verification hash so your
391
389
  secret may need to be shorter depending on the settings you choose.
@@ -426,44 +424,17 @@ The `HUMAN` format can be easily shared in a tweet, email, or even a URL. The
426
424
  easier to visually compare shares and see if they have matching identifiers and
427
425
  if you have enough shares to reach the threshold.
428
426
 
429
- The `pad_blocksize` arg takes an Integer between 0..255 inclusive. Your secret
430
- **MUST NOT** *begin* with this character (which was chosen to make less likely).
431
- The padding character used is `"\u001F"` `Unit Separator, decimal 31`.
432
-
433
- Padding is applied to the nearest multiple of the number of bytes specified.
434
- `pad_blocksize` defaults to no padding (0). For example:
435
-
436
- ```ruby
437
- padding_blocksize: 8
438
- (padded with zeros for illustration purposes)
439
-
440
- # a single char, padded up to 8
441
- 'a' -> "0000000a"
442
-
443
- # 8 chars, no padding needed to get to 8
444
- 'aaaaaaaa' -> "aaaaaaaa"
445
-
446
- # 9 chars, bumps blocksize up to 16 and pads
447
- 'aaaaaaaaa' -> "0000000aaaaaaaaa"
448
- ```
427
+ The `padding` arg accepts a Boolean to indicate whether to apply PKCS#7
428
+ padding to the secret string. This is applied and removed automatically
429
+ by default and padding defaults to a block size of 16 bytes. You probably
430
+ never need to use this option to turn it off unless you are trying to
431
+ trade shares with the Python implementation.
449
432
 
450
433
  Since TSS share data is essentially the same size as the original secret
451
- (with a known size header), padding smaller secrets may help mask the size
452
- of the secret itself from an attacker. Padding is not part of the RTSS spec
453
- so other TSS clients won't strip off the padding and may fail when recombining
454
- shares. If you need this level of interoperability you should probably skip
455
- the `pad_blocksize` padding and just pad the secret yourself prior to splitting
456
- it. You need to pad using a character other than `"\u001F"`.
457
-
458
- If you want to do padding this way, there is a utility method you can use
459
- to do that. This is the same method used internally.
460
-
461
- ```ruby
462
- # Util.left_pad(byte_multiple, input_string, pad_char = "\u001F")
463
-
464
- > Util.left_pad(16, 'abc', "0")
465
- => "0000000000000abc"
466
- ```
434
+ (with a known size header), the padding applied to smaller secrets may help
435
+ mask the exact size of the secret itself from an attacker. Padding is not part of
436
+ the RTSS spec so other TSS clients won't strip off the padding and may fail
437
+ when recombining shares.
467
438
 
468
439
  ### Example Usage
469
440
 
@@ -475,20 +446,20 @@ identifier = SecureRandom.hex(8)
475
446
  hash_alg = 'SHA256'
476
447
  format = 'HUMAN'
477
448
 
478
- s = TSS.split(secret: secret, threshold: threshold, num_shares: num_shares, identifier: identifier, hash_alg: 'SHA256', pad_blocksize: 16, format: format)
449
+ s = TSS.split(secret: secret, threshold: threshold, num_shares: num_shares, identifier: identifier, hash_alg: 'SHA256', format: format)
479
450
 
480
- => ["tss~v1~9fdfe2516240737e~3~OWZkZmUyNTE2MjQwNzM3ZQIDADEBe1METAFZDbpq7yHIFRvxr1PjBOLRnHzDyzDGrnDvYp90jMKPLWwo3eiWiuafRaYJ",
481
- "tss~v1~9fdfe2516240737e~3~OWZkZmUyNTE2MjQwNzM3ZQIDADECOeQaLXYx99N2SDKutj0_smSonMdMMzeJc_CWPRT8nppHkdsUc7hW6cSw_RDcaKMF",
482
- "tss~v1~9fdfe2516240737e~3~OWZkZmUyNTE2MjQwNzM3ZQIDADEDXagBfmgOlQY8xXIUg0SvZ-yYgORZzeWiyjRBmsDMLwG7bLmmswSAOllamqGZ-_fb",
483
- "tss~v1~9fdfe2516240737e~3~OWZkZmUyNTE2MjQwNzM3ZQIDADEExmXZHYJsLzipmqYryl5SDlhJzdZxLh_2y5bM_tOOmdm9rpws3izJqHrzmCHQi5mn",
484
- "tss~v1~9fdfe2516240737e~3~OWZkZmUyNTE2MjQwNzM3ZQIDADEFoinCTpxTTe3jF-aR_yfC29B50fVk0M3dclIbWQe-KEJBU_6eHpAfe-cZ_5CVGM15"]
451
+ => ["tss~v1~79923b087dab7fa2~3~Nzk5MjNiMDg3ZGFiN2ZhMgIDADEB2qA6IYq8yOGlPAl0B4MgRsVazZMWGLwRNgGMPKutOYbB0gjkVHNqbNYl-0l1f98W",
452
+ "tss~v1~79923b087dab7fa2~3~Nzk5MjNiMDg3ZGFiN2ZhMgIDADECvjwdUHc8MzqvIllR2Rj9TnnlN_2eRUzH6MUsd8ncua4jpXQ3FgM1hUmLHmrgHq0u",
453
+ "tss~v1~79923b087dab7fa2~3~Nzk5MjNiMDg3ZGFiN2ZhMgIDADEDAvNIUZ_hiftofyog257YDWds4q9MP14-rDCxQsauUyxqBtzur6Ch5-rSCHRPt4Dv",
454
+ "tss~v1~79923b087dab7fa2~3~Nzk5MjNiMDg3ZGFiN2ZhMgIDADEEF7zGEx0GSC6YLgVD6xcQispDCO_JTUSDFbsbpalopakh0FmTfmO-JJKGQSlJb1il",
455
+ "tss~v1~79923b087dab7fa2~3~Nzk5MjNiMDg3ZGFiN2ZhMgIDADEFq3OTEvXb8u9fc3Yy6ZE1ydTK3b0bN1Z6UU6GkKYaTytoc_FKx8AqRjHfVzfmxnVk"]
485
456
 
486
- secret = TSS.combine(shares: s)
457
+ secret = TSS.combine(shares: s)
487
458
 
488
- => {:hash=>"dbd318c1c462aee872f41109a4dfd3048871a03dedd0fe0e757ced57dad6f2d7",
459
+ => {:hash=>"dbd318c1c462aee872f41109a4dfd3048871a03dedd0fe0e757ced57dad6f2d7",
489
460
  :hash_alg=>"SHA256",
490
- :identifier=>"9fdfe2516240737e",
491
- :process_time=>0.77,
461
+ :identifier=>"79923b087dab7fa2",
462
+ :process_time=>0.92,
492
463
  :secret=>"foo bar baz",
493
464
  :threshold=>3}
494
465
  ```
@@ -558,7 +529,7 @@ many combinations (`2.88 * 10^75`) as there are Atoms in the Universe (`10^80`).
558
529
  If the combine operation does not result in a secret being successfully
559
530
  extracted, then a `TSS::Error` exception will be raised.
560
531
 
561
- A great short read on this is
532
+ A great short read on big numbers is
562
533
  [On the (Small) Number of Atoms in the Universe](http://norvig.com/atoms.html)
563
534
 
564
535
  ### Exception Handling
@@ -702,7 +673,7 @@ contributors are expected to adhere to the
702
673
 
703
674
  ### Copyright
704
675
 
705
- (c) 2016 Glenn Rempe <[glenn@rempe.us](mailto:glenn@rempe.us)> ([https://www.rempe.us/](https://www.rempe.us/))
676
+ (c) 2016-2017 Glenn Rempe <[glenn@rempe.us](mailto:glenn@rempe.us)> ([https://www.rempe.us/](https://www.rempe.us/))
706
677
 
707
678
  ### License
708
679
 
@@ -18,13 +18,12 @@ module Contracts
18
18
  class SecretArg
19
19
  def self.valid? val
20
20
  val.is_a?(String) &&
21
- val.length.between?(1,65502) &&
22
- ['UTF-8', 'US-ASCII'].include?(val.encoding.name) &&
23
- val.slice(0) != "\u001F"
21
+ val.length.between?(1,TSS::MAX_UNPADDED_SECRET_SIZE) &&
22
+ ['UTF-8', 'US-ASCII'].include?(val.encoding.name)
24
23
  end
25
24
 
26
25
  def self.to_s
27
- 'must be a UTF-8 or US-ASCII String between 1 and 65,502 characters in length and must not begin with the padding char \u001F'
26
+ "must be a UTF-8 or US-ASCII String between 1 and #{TSS::MAX_UNPADDED_SECRET_SIZE} characters in length"
28
27
  end
29
28
  end
30
29
 
@@ -6,6 +6,7 @@ module TSS
6
6
 
7
7
  method_option :input_file, :aliases => '-I', :banner => 'input_file', :type => :string, :desc => 'A filename to read shares from'
8
8
  method_option :output_file, :aliases => '-O', :banner => 'output_file', :type => :string, :desc => 'A filename to write the recovered secret to'
9
+ method_option :padding, :type => :boolean, :default => true, :desc => 'Whether PKCS#7 padding is expected in the secret and should be removed'
9
10
 
10
11
  desc 'combine', 'Enter shares to recover a split secret'
11
12
 
@@ -35,6 +36,12 @@ module TSS
35
36
  on the output file should provide a digest matching that of the secret
36
37
  when it was originally split.
37
38
 
39
+ padding/no-padding :
40
+ Whether or not PKCS#7 padding should be removed from secret data. By
41
+ default padding is applied to shared secrets when created. Turning this
42
+ off may be helpful if you need to combine shares created with a third-party
43
+ library.
44
+
38
45
  Example w/ options:
39
46
 
40
47
  $ tss combine -I shares.txt -O secret.txt
@@ -104,7 +111,7 @@ module TSS
104
111
  end
105
112
 
106
113
  begin
107
- sec = TSS.combine(shares: shares)
114
+ sec = TSS.combine(shares: shares, padding: options[:padding])
108
115
 
109
116
  say('')
110
117
  say('RECOVERED SECRET METADATA')
@@ -9,7 +9,7 @@ module TSS
9
9
  method_option :identifier, :aliases => '-i', :banner => 'identifier', :type => :string, :desc => 'A unique identifier string, 0-16 Bytes, [a-zA-Z0-9.-_]'
10
10
  method_option :hash_alg, :aliases => '-h', :banner => 'hash_alg', :type => :string, :desc => 'A hash type for verification, NONE, SHA1, SHA256'
11
11
  method_option :format, :aliases => '-f', :banner => 'format', :type => :string, :default => 'HUMAN', :desc => 'Share output format, BINARY or HUMAN'
12
- method_option :pad_blocksize, :aliases => '-p', :banner => 'pad_blocksize', :type => :numeric, :desc => 'Block size # secrets will be left-padded to, 0-255'
12
+ method_option :padding, :type => :boolean, :default => true, :desc => 'Whether to apply PKCS#7 padding to the secret'
13
13
  method_option :input_file, :aliases => '-I', :banner => 'input_file', :type => :string, :desc => 'A filename to read the secret from'
14
14
  method_option :output_file, :aliases => '-O', :banner => 'output_file', :type => :string, :desc => 'A filename to write the shares to'
15
15
 
@@ -20,8 +20,8 @@ module TSS
20
20
  a SECRET provided. A secret to be split can be provided using one of three
21
21
  different input methods; STDIN, a path to a file, or when prompted
22
22
  for it interactively. In all cases the secret should be UTF-8 or
23
- US-ASCII encoded text and be no larger than 65,535 Bytes (including header
24
- and hash verification bytes).
23
+ US-ASCII encoded text and be no larger than #{TSS::MAX_UNPADDED_SECRET_SIZE}
24
+ bytes.
25
25
 
26
26
  Optional Params:
27
27
 
@@ -41,8 +41,8 @@ module TSS
41
41
  hash_alg :
42
42
  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.
43
43
 
44
- pad_blocksize :
45
- An Integer, 0-255, that represents a multiple to which the secret will be padded. For example if pad_blocksize is set to 8, the secret 'abc' would be left-padded to '00000abc' (the padding char is not zero, that is just for illustration).
44
+ padding/no-padding :
45
+ Whether to apply PKCS#7 padding to secret. By default padding is applied. Turning this off may be helpful if you need to interoperate with a third party library.
46
46
 
47
47
  format :
48
48
  Whether to output the shares as a binary octet string (RTSS), or as more human friendly URL safe Base 64 encoded text with some metadata.
@@ -55,18 +55,19 @@ module TSS
55
55
 
56
56
  Example w/ options:
57
57
 
58
- $ tss split -t 3 -n 6 -i abc123 -h SHA256 -p 8 -f HUMAN
58
+ $ tss split -t 3 -n 6 -i abc123 -h SHA256 -f HUMAN
59
59
 
60
60
  Enter your secret:
61
61
 
62
62
  secret > my secret
63
63
 
64
- tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEBQ-AQG3PuU4oT4qHOh2oJmu-vQwGE6O5hsGRBNtdAYauTIi7VoIdi5imWSrswDdRy
65
- tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADECM0OK5TSamH3nubH3FJ2EGZ4Yux4eQC-mvcYY85oOe6ae3kpvVXjuRUDU1m6sX20X
66
- tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEDb7yF4Vhr1JqNe2Nc8IXo98hmKAxsqC3c_Mn3r3t60NxQMC22ate51StDOM-BImch
67
- tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEEIXU0FajldnRtEQMLK-ZYMO2MRa0NmkBFfNAOx7olbgXLkVbP9txXMDsdokblVwke
68
- tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEFfYo7EcQUOpMH09Ggz_403rvy1r9_ckI_Pd_hm1tRxX8FfzEWyXMAoFCKTOfIKgMo
69
- tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEGDSmh74Ng8WTziMGZXAm5XcpFLqDl2oP4MH24XhYf33IIg1WsPIyMAznI0DJUeLpN
64
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEB113xpF37jGHm5QGhXKD8mgK2897MIQkSWri6ksNnAODn0efXznuBsSUnhlDIqQFU
65
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEC4tZegQrC3z6-02er3FZaWMadtlvxPb1EI_FNjG0dFrcdEDj4V7Cmcw___SesJHHP
66
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEDWPKPVjJaITosPGAMhvCgxCBB9uptl2h5UPngnw71V7Z9T-pnxiLKIfgUbRqyBrv-
67
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEExY3ti8ckAIQC02OKCrpEVVnUmyg3NXO9oG3PNw3PlgbbKdFRi9gBCNN_tjkhT3An
68
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEFf6k8XP-8_oCQPGQtUBy-yb8I25mrn6aA02ViJG4n1we7dgPOGkptWiSUJgQ_bboW
69
+ tss~v1~abc123~3~YWJjMTIzAAAAAAAAAAAAAAIDADEGSiKTeaiFrd_ICgIn0OoYC3sjnhyWgxLWqiyVOsBdwVBBt9zhg4FKmA5MXXNb4MqN
70
+
70
71
  LONGDESC
71
72
 
72
73
  # rubocop:disable CyclomaticComplexity
@@ -124,8 +125,8 @@ module TSS
124
125
  args[:num_shares] = options[:num_shares] if options[:num_shares]
125
126
  args[:identifier] = options[:identifier] if options[:identifier]
126
127
  args[:hash_alg] = options[:hash_alg] if options[:hash_alg]
127
- args[:pad_blocksize] = options[:pad_blocksize] if options[:pad_blocksize]
128
128
  args[:format] = options[:format] if options[:format]
129
+ args[:padding] = options[:padding]
129
130
 
130
131
  begin
131
132
  log("Calling : TSS.split(#{args.inspect})")
@@ -12,14 +12,15 @@ module TSS
12
12
 
13
13
  C = Contracts
14
14
 
15
- attr_reader :shares, :select_by
15
+ attr_reader :shares, :select_by, :padding
16
16
 
17
- Contract ({ :shares => C::ArrayOfShares, :select_by => C::Maybe[C::SelectByArg] }) => C::Any
17
+ Contract ({ :shares => C::ArrayOfShares, :select_by => C::Maybe[C::SelectByArg], :padding => C::Maybe[C::Bool] }) => C::Any
18
18
  def initialize(opts = {})
19
19
  # clone the incoming shares so the object passed to this
20
20
  # function doesn't get modified.
21
21
  @shares = opts.fetch(:shares).clone
22
22
  @select_by = opts.fetch(:select_by, 'FIRST')
23
+ @padding = opts.fetch(:padding, true)
23
24
  end
24
25
 
25
26
  # Warning, you probably don't want to use this directly. Instead
@@ -150,8 +151,6 @@ module TSS
150
151
  secret << Util.lagrange_interpolation(u, v)
151
152
  end
152
153
 
153
- strip_left_pad(secret)
154
-
155
154
  hash_alg = Hasher.key_from_code(hash_id)
156
155
 
157
156
  # Run the hash digest checks if the shares were created with a digest
@@ -161,6 +160,10 @@ module TSS
161
160
  orig_hash_bytes = secret.pop(Hasher.bytesize(hash_alg))
162
161
  orig_hash_hex = Util.bytes_to_hex(orig_hash_bytes)
163
162
 
163
+ # Remove PKCS#7 padding from the secret now that the hash
164
+ # has been extracted from the data
165
+ secret = Util.unpad(secret) if padding
166
+
164
167
  # RTSS : verify that the recombined secret computes the same hash
165
168
  # digest now as when it was originally created.
166
169
  new_hash_bytes = Hasher.byte_array(hash_alg, Util.bytes_to_utf8(secret))
@@ -169,6 +172,8 @@ module TSS
169
172
  unless Util.secure_compare(orig_hash_hex, new_hash_hex)
170
173
  raise TSS::InvalidSecretHashError, 'invalid shares, hash of secret does not equal embedded hash'
171
174
  end
175
+ else
176
+ secret = Util.unpad(secret) if padding
172
177
  end
173
178
 
174
179
  if secret.present?
@@ -178,16 +183,6 @@ module TSS
178
183
  end
179
184
  end
180
185
 
181
- # Strip off leading padding chars ("\u001F", decimal 31)
182
- #
183
- # @param secret the secret to be stripped
184
- # @return returns the secret, stripped of the leading padding char
185
- # @raise [ParamContractError] if secret appears invalid
186
- Contract C::ArrayOf[C::Num] => C::Maybe[Array]
187
- def strip_left_pad(secret)
188
- secret.shift while secret.first == 31
189
- end
190
-
191
186
  # Do all of the shares match the pattern expected of human style shares?
192
187
  #
193
188
  # @param shares the shares to be evaluated
@@ -9,9 +9,9 @@ module TSS
9
9
 
10
10
  C = Contracts
11
11
 
12
- attr_reader :secret, :threshold, :num_shares, :identifier, :hash_alg, :format, :pad_blocksize
12
+ attr_reader :secret, :threshold, :num_shares, :identifier, :hash_alg, :format, :padding
13
13
 
14
- Contract ({ :secret => C::SecretArg, :threshold => C::Maybe[C::ThresholdArg], :num_shares => C::Maybe[C::NumSharesArg], :identifier => C::Maybe[C::IdentifierArg], :hash_alg => C::Maybe[C::HashAlgArg], :format => C::Maybe[C::FormatArg], :pad_blocksize => C::Maybe[C::PadBlocksizeArg] }) => C::Any
14
+ Contract ({ :secret => C::SecretArg, :threshold => C::Maybe[C::ThresholdArg], :num_shares => C::Maybe[C::NumSharesArg], :identifier => C::Maybe[C::IdentifierArg], :hash_alg => C::Maybe[C::HashAlgArg], :format => C::Maybe[C::FormatArg], :padding => C::Maybe[C::Bool] }) => C::Any
15
15
  def initialize(opts = {})
16
16
  @secret = opts.fetch(:secret)
17
17
  @threshold = opts.fetch(:threshold, 3)
@@ -19,7 +19,7 @@ module TSS
19
19
  @identifier = opts.fetch(:identifier, SecureRandom.hex(8))
20
20
  @hash_alg = opts.fetch(:hash_alg, 'SHA256')
21
21
  @format = opts.fetch(:format, 'HUMAN')
22
- @pad_blocksize = opts.fetch(:pad_blocksize, 0)
22
+ @padding = opts.fetch(:padding, true)
23
23
  end
24
24
 
25
25
  SHARE_HEADER_STRUCT = BinaryStruct.new([
@@ -61,15 +61,19 @@ module TSS
61
61
  def split
62
62
  num_shares_not_less_than_threshold!(threshold, num_shares)
63
63
 
64
- # RTSS : Combine the secret with a hash digest before splitting. On recombine
65
- # the two will be separated again and the hash used to validate the
66
- # correct secret was returned. secret || hash(secret). You can also
67
- # optionally pad the secret first.
68
- padded_secret = Util.left_pad(pad_blocksize, secret)
69
- hashed_secret = Hasher.byte_array(hash_alg, secret)
70
- secret_bytes = Util.utf8_to_bytes(padded_secret) + hashed_secret
64
+ # Append needed PKCS#7 padding to the string
65
+ secret_padded = padding ? Util.pad(secret) : secret
71
66
 
72
- secret_bytes_is_smaller_than_max_size!(secret_bytes)
67
+ # Calculate the cryptographic hash of the secret string
68
+ secret_hash = Hasher.byte_array(hash_alg, secret)
69
+
70
+ # RTSS : Combine the secret with a hash digest before splitting. When
71
+ # recombine the two will be separated again and the hash will be used
72
+ # to validate the correct secret was returned.
73
+ # secret || padding || hash(secret)
74
+ secret_pad_hash_bytes = Util.utf8_to_bytes(secret_padded) + secret_hash
75
+
76
+ secret_bytes_is_smaller_than_max_size!(secret_pad_hash_bytes)
73
77
 
74
78
  # For each share, a distinct Share Index is generated. Each Share
75
79
  # Index is an octet other than the all-zero octet. All of the Share
@@ -101,7 +105,7 @@ module TSS
101
105
  # of M octets; A[0] is equal to the first octet of the secret, B[0] is
102
106
  # equal to the second octet of the secret, and so on.
103
107
  #
104
- secret_bytes.each do |byte|
108
+ secret_pad_hash_bytes.each do |byte|
105
109
  # Unpack random Byte String into Byte Array of 8 bit unsigned Integers
106
110
  r = SecureRandom.random_bytes(threshold - 1).unpack('C*')
107
111
 
@@ -148,8 +152,8 @@ module TSS
148
152
  # @raise [ParamContractError, TSS::ArgumentError] if invalid
149
153
  Contract C::ArrayOf[C::Int] => C::Bool
150
154
  def secret_bytes_is_smaller_than_max_size!(secret_bytes)
151
- if secret_bytes.size >= 65_535
152
- raise TSS::ArgumentError, 'invalid secret, combined padded secret and hash are too large'
155
+ if secret_bytes.size > TSS::MAX_SECRET_SIZE
156
+ raise TSS::ArgumentError, 'invalid secret, combined padded and hashed secret is too large'
153
157
  else
154
158
  return true
155
159
  end
@@ -16,6 +16,16 @@ module TSS
16
16
  include Contracts::Core
17
17
  C = Contracts
18
18
 
19
+ # Defined in TSS spec, two less than 2^16
20
+ MAX_SECRET_SIZE = 64_534
21
+
22
+ # Max size minus up to 16 bytes PKCS#7 padding
23
+ # and 32 bytes of cryptographic hash
24
+ MAX_UNPADDED_SECRET_SIZE = MAX_SECRET_SIZE - 48
25
+
26
+ # When applying PKCS#7 padding, what block size in bytes should be used
27
+ PADDING_BLOCK_SIZE_BYTES = 16
28
+
19
29
  # An unexpected error has occurred.
20
30
  class Error < StandardError; end
21
31
 
@@ -59,23 +69,11 @@ module TSS
59
69
  # who are recombining the shares to verify if they have in fact recovered
60
70
  # the correct secret.
61
71
  # @option opts [String] :format ('BINARY') the format of the String share output, 'BINARY' or 'HUMAN'
62
- # @option opts [Integer] :pad_blocksize (0) An integer representing the nearest multiple of Bytes
63
- # to left pad the secret to. Defaults to not adding any padding (0). Padding
64
- # is done with the "\u001F" character (decimal 31 in a Byte Array).
65
- #
66
- # Since TSS share data (minus the header) is essentially the same size as the
67
- # original secret, padding smaller secrets may help mask the size of the
68
- # contents from an attacker. Padding is not part of the RTSS spec so other
69
- # TSS clients won't strip off the padding and may not validate correctly.
70
- #
71
- # If you need this interoperability you should probably pad the secret
72
- # yourself prior to splitting it and leave the default zero-length pad in
73
- # place. You would also need to manually remove the padding you added after
74
- # the share is recombined, or instruct recipients to ignore it.
72
+ # @option opts [Boolean] :padding Whether to apply PKCS#7 padding to secret
75
73
  #
76
74
  # @return an Array of formatted String shares
77
75
  # @raise [ParamContractError, TSS::ArgumentError] if the options Types or Values are invalid
78
- Contract ({ :secret => C::SecretArg, :threshold => C::Maybe[C::ThresholdArg], :num_shares => C::Maybe[C::NumSharesArg], :identifier => C::Maybe[C::IdentifierArg], :hash_alg => C::Maybe[C::HashAlgArg], :format => C::Maybe[C::FormatArg], :pad_blocksize => C::Maybe[C::PadBlocksizeArg] }) => C::ArrayOfShares
76
+ Contract ({ :secret => C::SecretArg, :threshold => C::Maybe[C::ThresholdArg], :num_shares => C::Maybe[C::NumSharesArg], :identifier => C::Maybe[C::IdentifierArg], :hash_alg => C::Maybe[C::HashAlgArg], :format => C::Maybe[C::FormatArg], :padding => C::Maybe[C::Bool] }) => C::ArrayOfShares
79
77
  def self.split(opts)
80
78
  TSS::Splitter.new(opts).split
81
79
  end
@@ -86,6 +84,7 @@ module TSS
86
84
  #
87
85
  # @param [Hash] opts the options to create a message with.
88
86
  # @option opts [Array<String>] :shares an Array of String shares to try to recombine into a secret
87
+ # @option opts [Boolean] :padding Whether PKCS#7 padding is expected in the secret and should be removed
89
88
  # @option opts [String] :select_by ('FIRST') the method to use for selecting
90
89
  # shares from the Array if more then threshold shares are provided. Can be
91
90
  # upper case 'FIRST', 'SAMPLE', or 'COMBINATIONS'.
@@ -122,7 +121,7 @@ module TSS
122
121
  # @raise [TSS::NoSecretError] if the secret cannot be re-created from the shares provided
123
122
  # @raise [TSS::InvalidSecretHashError] if the embedded hash of the secret does not match the hash of the recreated secret
124
123
  # @raise [ParamContractError, TSS::ArgumentError] if the options Types or Values are invalid
125
- Contract ({ :shares => C::ArrayOfShares, :select_by => C::Maybe[C::SelectByArg] }) => ({ :hash => C::Maybe[String], :hash_alg => C::HashAlgArg, :identifier => C::IdentifierArg, :process_time => C::Num, :secret => C::SecretArg, :threshold => C::ThresholdArg})
124
+ Contract ({ :shares => C::ArrayOfShares, :padding => C::Maybe[C::Bool], :select_by => C::Maybe[C::SelectByArg] }) => ({ :hash => C::Maybe[String], :hash_alg => C::HashAlgArg, :identifier => C::IdentifierArg, :process_time => C::Num, :secret => C::SecretArg, :threshold => C::ThresholdArg })
126
125
  def self.combine(opts)
127
126
  TSS::Combiner.new(opts).combine
128
127
  end
@@ -271,18 +271,40 @@ module TSS
271
271
  bytes_to_hex(utf8_to_bytes(str))
272
272
  end
273
273
 
274
- # Left pad a String with pad_char in multiples of byte_multiple
274
+ # Pad a String with PKCS#7 (RFC5652)
275
+ # See : https://tools.ietf.org/html/rfc5652#section-6.3
275
276
  #
276
- # @param byte_multiple pad in blocks of this size
277
- # @param input_string the String to pad
278
- # @param pad_char the String to pad with
279
- # @return a padded String
280
- Contract C::Int, String, String => String
281
- def self.left_pad(byte_multiple, input_string, pad_char = "\u001F")
282
- return input_string if byte_multiple == 0
283
- pad_length = byte_multiple - (input_string.length % byte_multiple)
284
- return input_string if pad_length == byte_multiple
285
- (pad_char * pad_length) + input_string
277
+ # @param str the String or Array of bytes to pad
278
+ # @param k pad blocksize (0-255), default 16
279
+ # @return a PKCS#7 padded String or Array of bytes
280
+ Contract C::Or[Array, String], C::PadBlocksizeArg => C::Or[Array, String]
281
+ def self.pad(str, k = TSS::PADDING_BLOCK_SIZE_BYTES)
282
+ return str if k.zero?
283
+ str_bytes = str.is_a?(Array) ? str : TSS::Util.utf8_to_bytes(str)
284
+ l = str_bytes.length
285
+ val = k - (l % k)
286
+ pad_bytes = [val] * val
287
+ padded_str_bytes = str_bytes + pad_bytes
288
+ str.is_a?(Array) ? padded_str_bytes : TSS::Util.bytes_to_utf8(padded_str_bytes)
289
+ end
290
+
291
+ # Remove padding from a String previously padded with PKCS#7 (RFC5652)
292
+ #
293
+ # @param str the String to remove padding from
294
+ # @param k pad blocksize (0-255)
295
+ # @return an unpadded String or Array of bytes
296
+ Contract C::Or[Array, String], C::PadBlocksizeArg => C::Or[Array, String]
297
+ def self.unpad(str, k = TSS::PADDING_BLOCK_SIZE_BYTES)
298
+ return str if k.zero?
299
+ str_bytes = str.is_a?(Array) ? str : TSS::Util.utf8_to_bytes(str)
300
+ val = str_bytes.last
301
+ raise 'Input is not padded or padding is corrupt' if val > k
302
+ # Verify that the proper number of PKCS#7 padding bytes are present
303
+ # and match the last byte value in both value and number of bytes present.
304
+ raise 'Padding bytes are invalid' unless str_bytes.last(val).all? {|b| b == val}
305
+ l = str_bytes.length - val
306
+ unpadded_str_bytes = str_bytes.take(l)
307
+ str.is_a?(Array) ? unpadded_str_bytes : TSS::Util.bytes_to_utf8(unpadded_str_bytes)
286
308
  end
287
309
 
288
310
  # Constant time string comparison.
@@ -1,3 +1,3 @@
1
1
  module TSS
2
- VERSION = '0.4.2'.freeze
2
+ VERSION = '0.5.0'.freeze
3
3
  end
@@ -48,8 +48,10 @@ Gem::Specification.new do |spec|
48
48
  spec.executables << 'tss'
49
49
  spec.require_paths = ['lib']
50
50
 
51
+ spec.metadata["yard.run"] = "yri" # use "yard" to build full HTML docs.
52
+
51
53
  spec.add_dependency 'activesupport', '>= 4.0.0'
52
- spec.add_dependency 'sysrandom', '>= 1.0.3', '~> 1.0.3'
54
+ spec.add_dependency 'sysrandom', '>= 1.0.3', '~> 1.0.4'
53
55
  spec.add_dependency 'contracts', '~> 0.14'
54
56
  spec.add_dependency 'binary_struct', '~> 2.1'
55
57
  spec.add_dependency 'thor', '~> 0.19'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Glenn Rempe
@@ -30,7 +30,7 @@ cert_chain:
30
30
  vprF5QiDz8HshVP9DjJT2I1wyGyvxEdU3cTRo0upMP/VZLcgyBVFy90N2XYWWk2D
31
31
  GIxGSw==
32
32
  -----END CERTIFICATE-----
33
- date: 2016-10-13 00:00:00.000000000 Z
33
+ date: 2017-01-29 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: activesupport
@@ -55,7 +55,7 @@ dependencies:
55
55
  version: 1.0.3
56
56
  - - "~>"
57
57
  - !ruby/object:Gem::Version
58
- version: 1.0.3
58
+ version: 1.0.4
59
59
  type: :runtime
60
60
  prerelease: false
61
61
  version_requirements: !ruby/object:Gem::Requirement
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: 1.0.3
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.0.3
68
+ version: 1.0.4
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: contracts
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -281,7 +281,8 @@ files:
281
281
  homepage: https://github.com/grempe/tss-rb
282
282
  licenses:
283
283
  - MIT
284
- metadata: {}
284
+ metadata:
285
+ yard.run: yri
285
286
  post_install_message:
286
287
  rdoc_options: []
287
288
  require_paths:
@@ -298,7 +299,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
299
  version: '0'
299
300
  requirements: []
300
301
  rubyforge_project:
301
- rubygems_version: 2.5.1
302
+ rubygems_version: 2.6.8
302
303
  signing_key:
303
304
  specification_version: 4
304
305
  summary: A Ruby gem implementing Threshold Secret Sharing. This code can be used in
metadata.gz.sig CHANGED
@@ -1 +1 @@
1
- �)#�g9 5|O'J#F��ng#���O4� �NCj�":��)!���|����Y��P$���$(Ց�d-=�s��f�*%�ʛ(A(� ><��� �P(��oyP��3½���Jh�2�� �؀���4��KJ���Exnݞ��?r��e��,IA���<��(h4���(��6io����>��@J���=�XId����W"Yy��z�/��Tk��_'�����6�W�N�ܭ�$��������7
1
+ <��'