class TSS::Splitter
Warning, you probably don't want to use this directly. Instead see the TSS
module.
TSS::Splitter
has responsibility for splitting a secret into an Array of String shares.
Constants
- C
- SHARE_HEADER_STRUCT
Attributes
format[R]
hash_alg[R]
identifier[R]
padding[R]
secret[R]
threshold[R]
Public Class Methods
new(opts = {})
click to toggle source
# File lib/tss/splitter.rb, line 15 def initialize(opts = {}) @secret = opts.fetch(:secret) @threshold = opts.fetch(:threshold, 3) @num_shares = opts.fetch(:num_shares, 5) @identifier = opts.fetch(:identifier, SecureRandom.hex(8)) @hash_alg = opts.fetch(:hash_alg, 'SHA256') @format = opts.fetch(:format, 'HUMAN') @padding = opts.fetch(:padding, true) end
Public Instance Methods
split()
click to toggle source
# File lib/tss/splitter.rb, line 61 def split num_shares_not_less_than_threshold!(threshold, num_shares) # Append needed PKCS#7 padding to the string secret_padded = padding ? Util.pad(secret) : secret # Calculate the cryptographic hash of the secret string secret_hash = Hasher.byte_array(hash_alg, secret) # RTSS : Combine the secret with a hash digest before splitting. When # recombine the two will be separated again and the hash will be used # to validate the correct secret was returned. # secret || padding || hash(secret) secret_pad_hash_bytes = Util.utf8_to_bytes(secret_padded) + secret_hash secret_bytes_is_smaller_than_max_size!(secret_pad_hash_bytes) # For each share, a distinct Share Index is generated. Each Share # Index is an octet other than the all-zero octet. All of the Share # Indexes used during a share generation process MUST be distinct. # Each share is initialized to the Share Index associated with that # share. shares = [] (1..num_shares).each { |n| shares << [n] } # For each octet of the secret, the following steps are performed. # # An array A of M octets is created, in which the array element A[0] # contains the octet of the secret, and the array elements A[1], # ..., A[M-1] contain octets that are selected independently and # uniformly at random. # # For each share, the value of f(X,A) is # computed, where X is the Share Index of the share, and the # resulting octet is appended to the share. # # After the procedure is done, each share contains one more octet than # does the secret. The share format can be illustrated as # # +---------+---------+---------+---------+---------+ # | X | f(X,A) | f(X,B) | f(X,C) | ... | # +---------+---------+---------+---------+---------+ # # where X is the Share Index of the share, and A, B, and C are arrays # of M octets; A[0] is equal to the first octet of the secret, B[0] is # equal to the second octet of the secret, and so on. # secret_pad_hash_bytes.each do |byte| # Unpack random Byte String into Byte Array of 8 bit unsigned Integers r = SecureRandom.random_bytes(threshold - 1).unpack('C*') # Build each share one byte at a time for each byte of the secret. shares.map! { |s| s << Util.f(s[0], [byte] + r) } end # build up a common binary struct header for all shares header = share_header(identifier, hash_alg, threshold, shares.first.length) # create each binary or human share and return it. shares.map! do |s| binary = (header + s.pack('C*')).force_encoding('ASCII-8BIT') # join with URL safe '~' human = ['tss', 'v1', identifier, threshold, Base64.urlsafe_encode64(binary)].join('~') format == 'BINARY' ? binary : human end return shares end
Private Instance Methods
secret_bytes_is_smaller_than_max_size!(secret_bytes)
click to toggle source
# File lib/tss/splitter.rb, line 154 def secret_bytes_is_smaller_than_max_size!(secret_bytes) if secret_bytes.size > TSS::MAX_SECRET_SIZE raise TSS::ArgumentError, 'invalid secret, combined padded and hashed secret is too large' else return true end end