lib/tapyrus/slip39/sss.rb in tapyrus-0.3.4 vs lib/tapyrus/slip39/sss.rb in tapyrus-0.3.5

- old
+ new

@@ -1,6 +1,6 @@ -require 'securerandom' +require "securerandom" module Tapyrus module SLIP39 # Shamir's Secret Sharing class SSS @@ -29,37 +29,37 @@ # @param [Integer] group_threshold threshold number of group shares required to reconstruct the master secret. # @param [Integer] exp Iteration exponent. default is 0. # @param [String] secret master secret with hex format. # @param [String] passphrase the passphrase used for encryption/decryption. # @return [Array[Array[Tapyrus::SLIP39::Share]]] array of group shares. - def self.setup_shares(groups: [], group_threshold: nil, exp: 0, secret: nil, passphrase: '') - raise ArgumentError, 'Groups is empty.' if groups.empty? - raise ArgumentError, 'Group threshold must be greater than 0.' if group_threshold.nil? || group_threshold < 1 - raise ArgumentError, 'Master secret does not specified.' unless secret + def self.setup_shares(groups: [], group_threshold: nil, exp: 0, secret: nil, passphrase: "") + raise ArgumentError, "Groups is empty." if groups.empty? + raise ArgumentError, "Group threshold must be greater than 0." if group_threshold.nil? || group_threshold < 1 + raise ArgumentError, "Master secret does not specified." unless secret if (secret.htb.bytesize * 8) < MIN_STRENGTH_BITS raise ArgumentError, "The length of the master secret (#{secret.htb.bytesize} bytes) must be at least #{MIN_STRENGTH_BITS / 8} bytes." end unless secret.bytesize.even? - raise ArgumentError, 'The length of the master secret in bytes must be an even number.' + raise ArgumentError, "The length of the master secret in bytes must be an even number." end unless passphrase.ascii_only? - raise ArgumentError, 'The passphrase must contain only printable ASCII characters (code points 32-126).' + raise ArgumentError, "The passphrase must contain only printable ASCII characters (code points 32-126)." end if group_threshold > groups.length raise ArgumentError, "The requested group threshold (#{group_threshold}) must not exceed the number of groups (#{groups.length})." end groups.each do |threshold, count| - raise ArgumentError, 'Group threshold must be greater than 0.' if threshold.nil? || threshold < 1 + raise ArgumentError, "Group threshold must be greater than 0." if threshold.nil? || threshold < 1 if threshold > count raise ArgumentError, "The requested member threshold (#{threshold}) must not exceed the number of share (#{count})." end if threshold == 1 && count > 1 raise ArgumentError, - 'Creating multiple member shares with member threshold 1 is not allowed. Use 1-of-1 member sharing instead.' + "Creating multiple member shares with member threshold 1 is not allowed. Use 1-of-1 member sharing instead." end end id = SecureRandom.random_number(32_767) # 32767 is max number for 15 bits. ems = encrypt(secret, passphrase, exp, id) @@ -95,28 +95,28 @@ # master_secret = Tapyrus::SLIP39::SSS.recover_secret(shares, passphrase: 'xxx') # # @param [Array[Tapyrus::SLIP30::Share]] shares an array of shares. # @param [String] passphrase the passphrase using decrypt master secret. # @return [String] a master secret. - def self.recover_secret(shares, passphrase: '') - raise ArgumentError, 'share is empty.' if shares.nil? || shares.empty? + def self.recover_secret(shares, passphrase: "") + raise ArgumentError, "share is empty." if shares.nil? || shares.empty? groups = {} id = shares[0].id exp = shares[0].iteration_exp group_threshold = shares.first.group_threshold group_count = shares.first.group_count shares.each do |share| - raise ArgumentError, 'Invalid set of shares. All shares must have the same id.' unless id == share.id + raise ArgumentError, "Invalid set of shares. All shares must have the same id." unless id == share.id unless group_threshold == share.group_threshold - raise ArgumentError, 'Invalid set of shares. All shares must have the same group threshold.' + raise ArgumentError, "Invalid set of shares. All shares must have the same group threshold." end unless group_count == share.group_count - raise ArgumentError, 'Invalid set of shares. All shares must have the same group count.' + raise ArgumentError, "Invalid set of shares. All shares must have the same group count." end unless exp == share.iteration_exp - raise ArgumentError, 'Invalid set of shares. All Shares must have the same iteration exponent.' + raise ArgumentError, "Invalid set of shares. All Shares must have the same iteration exponent." end groups[share.group_index] ||= [] groups[share.group_index] << share end @@ -132,28 +132,28 @@ else value_length = shares.first.value.length x_coordinates = [] shares.each do |share| unless member_threshold == share.member_threshold - raise ArgumentError, 'Invalid set of shares. All shares in a group must have the same member threshold.' + raise ArgumentError, "Invalid set of shares. All shares in a group must have the same member threshold." end unless value_length == share.value.length - raise ArgumentError, 'Invalid set of shares. All share values must have the same length.' + raise ArgumentError, "Invalid set of shares. All share values must have the same length." end x_coordinates << share.member_index end x_coordinates.uniq! unless x_coordinates.size == shares.size - raise ArgumentError, 'Invalid set of shares. Share indices must be unique.' + raise ArgumentError, "Invalid set of shares. Share indices must be unique." end interpolate_shares = shares.map { |s| [s.member_index, s.value] } secret = interpolate(interpolate_shares, SECRET_INDEX) digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb digest, random_value = digest_value[0...DIGEST_LENGTH_BYTES].bth, digest_value[DIGEST_LENGTH_BYTES..-1].bth recover_digest = create_digest(secret, random_value) - raise ArgumentError, 'Invalid digest of the shared secret.' unless digest == recover_digest + raise ArgumentError, "Invalid digest of the shared secret." unless digest == recover_digest group_shares[group_index] = secret end end @@ -167,11 +167,11 @@ interpolate_shares = group_shares.map { |k, v| [k, v] } secret = interpolate(interpolate_shares, SECRET_INDEX) digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb digest, random_value = digest_value[0...DIGEST_LENGTH_BYTES].bth, digest_value[DIGEST_LENGTH_BYTES..-1].bth recover_digest = create_digest(secret, random_value) - raise ArgumentError, 'Invalid digest of the shared secret.' unless digest == recover_digest + raise ArgumentError, "Invalid digest of the shared secret." unless digest == recover_digest decrypt(secret, passphrase, exp, id) end private @@ -184,11 +184,11 @@ s = shares.find { |s| s[0] == x } return s[1] if s log_prod = shares.sum { |s| LOG_TABLE[s[0] ^ x] } - result = ('00' * shares.first[1].length).htb + result = ("00" * shares.first[1].length).htb shares.each do |share| log_basis_eval = (log_prod - LOG_TABLE[share[0] ^ x] - shares.sum { |s| LOG_TABLE[share[0] ^ s[0]] }) % 255 result = share[1] .htb @@ -211,11 +211,11 @@ def self.decrypt(ems, passphrase, exp, id) l, r = ems[0...(ems.length / 2)].htb, ems[(ems.length / 2)..-1].htb salt = get_salt(id) e = (Tapyrus::SLIP39::BASE_ITERATION_COUNT << exp) / Tapyrus::SLIP39::ROUND_COUNT Tapyrus::SLIP39::ROUND_COUNT.times.to_a.reverse.each do |i| - f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256') + f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, "sha256") l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize) end (r + l).bth end @@ -229,11 +229,11 @@ s = secret.htb l, r = s[0...(s.bytesize / 2)], s[(s.bytesize / 2)..-1] salt = get_salt(id) e = (Tapyrus::SLIP39::BASE_ITERATION_COUNT << exp) / Tapyrus::SLIP39::ROUND_COUNT Tapyrus::SLIP39::ROUND_COUNT.times.to_a.each do |i| - f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256') + f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, "sha256") l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize) end (r + l).bth end @@ -248,10 +248,10 @@ # get salt using encryption/decryption form id. # @param [Integer] id id # @return [String] salt with binary format. def self.get_salt(id) - (Tapyrus::SLIP39::CUSTOMIZATION_STRING.pack('c*') + id.itb) + (Tapyrus::SLIP39::CUSTOMIZATION_STRING.pack("c*") + id.itb) end # Split the share into +count+ with threshold +threshold+. # @param [Integer] threshold the threshold. # @param [Integer] count split count.