def pseudo_share_participant(self, i_secret, q_group, participant): """ pseudo share generation for a single participant U = h(x || i_U || q_v) """ print('Pseudo share computation for secret s%r, access group A%r,' 'participant P%r' % (i_secret, q_group, participant)) # l = length of longest access group for this secret lengths = [] gamma = self.access_structures[i_secret] for A in gamma: lengths.append(len(A)-1) l = max(lengths) u = floor(log2(self.k)) + 1 # u = bit length of number of secrets k v = floor(log2(l)) + 1 # v = bit length of l # concatenate x, i and q binary if isinstance(self.master_shares_x[participant-1], bytes): bytes_x = self.master_shares_x[participant-1] else: bytes_x = bytes([ self.master_shares_x[participant-1] ]) bytes_i = bytes([i_secret]) bytes_q = bytes([q_group]) message = b''.join([bytes_x, bytes_i, bytes_q]) # python 3.x # hash the concatenated bytes hash_of_message = common.hash(message, self.hash_len, self.hash_aes_nonce) share = common.modulo_p(self.p, hash_of_message) #print('Pseudo share for secret s%d, access group A%d, participant P%d:\nU = ' % (i_secret, q_group, participant), share.hex()) return share
def pseudo_share_participant(self, i_secret, q_group, participant): """ pseudo share generation for a single participant U = hash(master_share_x) XOR master_share_x """ print('Pseudo share computation for secret s%r, access group A%r,' 'participant P%r' % (i_secret, q_group, participant)) # convert master share to bytes if isinstance(self.master_shares_x[participant - 1], bytes): bytes_x = self.master_shares_x[participant - 1] int_x = int.from_bytes(self.master_shares_x[participant - 1], byteorder='big') else: bytes_x = bytes([self.master_shares_x[participant - 1]]) int_x = self.master_shares_x[participant - 1] # hash the master share hash_of_master_share = common.hash(bytes_x, self.hash_len, self.hash_aes_nonce) hash_of_master_share = common.modulo_p(self.p, hash_of_master_share) hash_of_master_share_int = int.from_bytes(hash_of_master_share, byteorder='big') # XOR hashed value with master share int_pseudo_share = hash_of_master_share_int ^ int_x print('XOR output =', int_pseudo_share) int_pseudo_share = common.modulo_p(self.p, int_pseudo_share) pseudo_share = int_pseudo_share.to_bytes( bytehelper.bytelen(int_pseudo_share), byteorder='big') assert isinstance(pseudo_share, bytes) return pseudo_share
def choose_distinct_master_shares_x(self): """ dealer chooses distinct master share x_j for each participant """ master_shares_x = common.list_of_random_in_modulo_p(self.n, self.hash_len, self.p) common.print_list_of_hex(master_shares_x, 'x') return master_shares_x # TODO: use yield to construct a generator
def split_secrets(self): """ Split secret in one step with Lin-Yeh algorithm """ self.random_id = common.provide_id(self.n, self.hash_len, self.p) self.master_shares_x = common.list_of_random_in_modulo_p( self.n, self.hash_len, self.p) self.access_group_polynomial_coeffs() self.compute_all_pseudo_shares() self.compute_all_public_shares_M() return self.pseudo_shares
def test_hash_same(): """ set up Dealer object and check hash repeatability """ # Create a Dealer dealer = Dealer(p256, n_participants, s_secrets, access_structures) # test hash function - it should be repeatable for the same Dealer object hash1 = common.hash(b'BYTESEQUENCE', dealer.hash_len, dealer.hash_aes_nonce) hash2 = common.hash(b'BYTESEQUENCE', dealer.hash_len, dealer.hash_aes_nonce) assert_equal(hash1, hash2)
def test_list_of_random_in_modulo_p(): """ test: list_of_random_in_modulo_p """ dealer = Dealer(4099, n_participants, s_secrets, access_structures) n = 5 randomList = common.list_of_random_in_modulo_p(n, dealer.hash_len, dealer.p) common.print_list_of_hex(randomList, 'test randomList') # check the length of the list assert_equal(len(randomList), n) # check the type of object in the list assert_equal(isinstance(randomList[0], bytes), True)
def test_hash_different(): """ different instances of Dealer should have different hash results as they have separate random AES nonces""" # Create a Dealer dealer1 = Dealer(p256, n_participants, s_secrets, access_structures) dealer2 = Dealer(p256, n_participants, s_secrets, access_structures) # test hash function - it should be different for distinct Dealers hash1 = common.hash(b'BYTESEQUENCE', dealer1.hash_len, dealer1.hash_aes_nonce) hash2 = common.hash(b'BYTESEQUENCE', dealer2.hash_len, dealer2.hash_aes_nonce) assert_not_equal(hash1, hash2)
def test_shamir_polynomial_compute(): dealer = Dealer(p256, n_participants, s_secrets, access_structures) dealer.access_group_polynomial_coeffs() # override coeffs dealer.d[0][0] = [bytes([0x05]), bytes([0x07])] coeffs = dealer.get_d_polynomial_coeffs(0, 0) secret_value = dealer.s_secrets[0] value = common.shamir_polynomial_compute(bytes([0x01]), coeffs, secret_value, dealer.p) assert_equal(value, 12 + s_secrets[0]) value = common.shamir_polynomial_compute(bytes([0x02]), coeffs, secret_value, dealer.p) assert_equal(value, 38 + s_secrets[0])
def test_access_group_polynomial_coeffs(): """ test: access_group_polynomial_coeffs """ A1 = (1, 3) # gamma1 is a group of users authorized to reconstruct s1 gamma1 = [A1] gamma2 = [(1, 2), (2, 3)] # A1, A2 implicitly gamma3 = [(1, 2, 3) ] # to secret s3 only all 3 users together can gain access access_structures = [gamma1, gamma2, gamma3] # Create a Dealer dealer = Dealer(p256, n_participants, s_secrets, access_structures) # compute d coeffs dealer.access_group_polynomial_coeffs() # dealer.print_list_of_hex(dealer.d[1][1], 'd-1-1') # test output assert_equal(len(dealer.d), 3) assert_equal(len(dealer.d[0]), 1) assert_equal(len(dealer.d[1][1]), 1) assert_equal(len(dealer.d[2][0]), 2) # Test index out of range with assert_raises(IndexError): print('Test', common.print_list_of_hex(dealer.d[2][1], 'd-2-1'))
def test_provide_id(): dealer = Dealer(p256, n_participants, s_secrets, access_structures) id_list = common.provide_id(n_participants, dealer.hash_len, dealer.p) # check the length of the list assert_equal(len(id_list), n_participants) # check the type of object in the list assert_equal(isinstance(id_list[0], bytes), True)
def split_secrets(self): """ Split secret in one step """ self.random_id = common.provide_id(self.n, self.hash_len, self.p) self.master_shares_x = self.choose_distinct_master_shares_x() self.access_group_polynomial_coeffs() self.compute_all_pseudo_shares() self.compute_all_public_shares_M() return self.pseudo_shares
def user_polynomial_value_B(self, i_secret, q_group, participant): assert(participant in self.access_structures[i_secret][q_group]) print('user_polynomial_value_B for secret %d, group A %d '.format(i_secret, q_group)) participant_id = self.random_id[participant-1] print('B value for user %d with ID %d'.format(participant, participant_id)) coeffs = self.get_d_polynomial_coeffs(i_secret, q_group) secret_value = self.s_secrets[i_secret] # returns int return common.shamir_polynomial_compute(participant_id, coeffs, secret_value, self.p)
def access_group_polynomial_coeffs(self): """ for the qth qualified set of access group, the dealer chooses d0, d1, d2... dm in Zp modulo field to construct the polynomial f_q(x) = si + d1*x + d2*x^2 + ... + dm*x^(m-1) note: d0 corresponds to x^1, d1 corresponds to x^2 """ for gindex, gamma in enumerate(self.access_structures): print('gamma%d for secret s%d:' % (gindex, gindex)) coeffs_for_A = [] self.d.append([]) for index, A in enumerate(gamma): coeffs_for_A = common.list_of_random_in_modulo_p( len(A) - 1, self.hash_len, self.p) print('A%d: %r' % (index, A)) common.print_list_of_hex(coeffs_for_A, 'polynomial coeff d') self.d[gindex].append(coeffs_for_A) return self.d
def combine_secret(self, i_secret, q_group, obtained_pseudo_shares): """ combine a single secret in Lin-Yeh algorithm """ if isinstance(obtained_pseudo_shares[0], bytes): obtained_shares_int = [] for obtained_share in obtained_pseudo_shares: obtained_shares_int.append( int.from_bytes(obtained_share, byteorder='big')) obtained_pseudo_shares = obtained_shares_int print('Obtained pseudo shares:', obtained_pseudo_shares) print('Access group:', self.access_structures[i_secret]) assert (q_group <= len(self.access_structures[i_secret])) combine_sum = 0 for b, Pb in enumerate(self.access_structures[i_secret][q_group]): print('\tb =', b) part_sum_B = (obtained_pseudo_shares[b] + self.public_shares_M[i_secret][q_group][b]) % self.p print('\tB = U+M, B = %d, M=%d' % (part_sum_B, self.public_shares_M[i_secret][q_group][b])) combine_product = 1 for r, Pr in enumerate(self.access_structures[i_secret][q_group]): if r != b: print('\t\tr =', r) print('\t\tID_(b=%d) : %d, ID_(r=%d) : %d' % (Pb, self.get_id_int(Pb), Pr, self.get_id_int(Pr))) denominator = (self.get_id_int(Pr) - self.get_id_int(Pb)) % self.p den_inverse = bytehelper.inverse_modulo_p( denominator, self.p) print('\t\tdenominator = %d\n its inverse = %d' % (denominator, den_inverse)) part_product = ( (self.get_id_int(Pr)) % self.p * den_inverse) % self.p combine_product *= part_product print('\t\tpart_product', part_product) print('\t\tcombine_product', combine_product) combine_sum += (part_sum_B * combine_product) % self.p print('\tcomb prod=%d, part_sum_B=%d, combined_sum=%d' % (combine_product, part_sum_B, combine_sum)) print("Combined sum, s%d = %d" % (i_secret, combine_sum % self.p)) return common.modulo_p(self.p, combine_sum)
def split_secrets(self): """ High-level interface function. Use Shamir's scheme to share the keys used to encrypt secrets. """ self.cipher_generate_keys() assert self.cipher_keys # Shamir polynomial for each access group self.access_group_polynomial_coeffs() assert self.d # ID for each participant self.random_id = common.provide_id(self.n, self.hash_len, self.p) self.cipher_encrypt_all_secrets() return self.compute_all_key_shares()
def combine_secret_key(self, i_secret, obtained_shares): """ combine a single key in Herraz-Ruiz-Saez algorithm """ print('Obtained pseudo shares:', obtained_shares) q_group = 0 combine_sum = 0 print('combine_secret_key for access structure {}', self.access_structures) for b, Pb in enumerate(self.access_structures[i_secret][q_group]): print('\tcurrent user {} with index {} ='.format(Pb, b)) part_sum = obtained_shares[b] % self.p combine_product = 1 for r, Pr in enumerate(self.access_structures[i_secret][q_group]): if r != b: print('\t\tr =', r) print('\t\tID_(b=%d) : %d, ID_(r=%d) : %d' % (Pb, self.get_id_int(Pb), Pr, self.get_id_int(Pr))) denominator = (self.get_id_int(Pr) - self.get_id_int(Pb)) % self.p den_inverse = bytehelper.inverse_modulo_p( denominator, self.p) print('\t\tdenominator = %d\n its inverse = %d' % (denominator, den_inverse)) part_product = ( (self.get_id_int(Pr)) % self.p * den_inverse) % self.p combine_product *= part_product print('\t\tpart_product', part_product) print('\t\tcombine_product', combine_product) combine_sum += (part_sum * combine_product) % self.p print('\tcomb prod=%d, part_sum_B=%d, combined_sum=%d' % (combine_product, part_sum, combine_sum)) print("Combined sum, s%d = %d" % (i_secret, combine_sum % self.p)) return common.modulo_p(self.p, combine_sum)
def main(): """ This example shows low-level functions for splitting and combining secrets with multi-secret sharing scheme by Roy & Adhikari. """ # large prime from NIST P-256 elliptic curve p256 = 2**256 - 2**224 + 2**192 + 2**96 - 1 # multi secret sharing parameters s_secrets = [7, 313, 671] # s_i n_participants = 3 # access structure: to which secret what group has access # Gamma(s_i) = [A1, A2, ... Al], Aq = (P1, P2, Pm), q=1,2...l A1 = [1, 3] # gamma1 is a group of users authorized to reconstruct s1 gamma1 = [A1] gamma2 = [[1, 2], [2, 3]] # A1, A2 implicitly gamma3 = [[1, 2, 3]] # to secret s3 only all 3 users together can gain access access_structures = [gamma1, gamma2, gamma3] # TODO: in GUI choosing access structures on a matrix # Create a Dealer dealer = multisecret.MultiSecretRoyAdhikari.Dealer(p256, n_participants, s_secrets, access_structures) dealer.random_id = common.provide_id( n_participants, dealer.hash_len, dealer.p) # a list of IDs stored internally dealer.master_shares_x = dealer.choose_distinct_master_shares_x() dealer.access_group_polynomial_coeffs() dealer.compute_all_pseudo_shares() dealer.compute_all_public_shares_M() print('obtained shares', dealer.pseudo_shares[0][0]) obtained_shares = [] for share in dealer.pseudo_shares[0][0]: obtained_shares.append(int.from_bytes(share, byteorder='big')) print('obtained shares', obtained_shares) combined_secret = dealer.combine_secret(0, 0, obtained_shares) print('Combined secret s1', combined_secret)
def compute_all_key_shares(self): self.key_shares = copy.deepcopy(self.access_structures) print(self.access_structures) for i, gamma in enumerate(self.access_structures): for q, A in enumerate(self.access_structures[i]): for b, Pb in enumerate(self.access_structures[i][q]): print('secret_value (cipher_key)', self.cipher_keys[i]) secret_value = int.from_bytes(self.cipher_keys[i], byteorder='big') self.key_shares[i][q][b] = \ common.shamir_polynomial_compute(self.random_id[b], self.d[i][q], secret_value, self.p) print( 'Key share = {} for user {} (index {}) and secret {}'. format(self.key_shares[i][q][b], Pb, b, i)) return self.key_shares
def test_modulo_p(): p = 1009 dealer = Dealer(p, n_participants, s_secrets, access_structures) assert_equal(common.modulo_p(p, 2011), 1002)