def psu_reconstruct_single_mutual_mask(client_index_drop, client_index_live, union_s_shares_drop, union_live_spk, \ UNION_DHKE, union_security_para_dict): """ Assistant function for reconstructing mutual masks in parallel. (Single process) """ # Load parameters for PRNG seed_len = union_security_para_dict['seed_len'] security_strength = union_security_para_dict['security_strength'] modulo_r_len = union_security_para_dict['modulo_r_len'] item_count = union_security_para_dict[ 'item_count'] # length of perturbed Bloom filter (x) union_drop_ssk = SecretSharer.recover_secret(union_s_shares_drop) # Derive seed for mutual mask, i.e., agreed key, (u, v via Diffie-Hellman Agreement) s_uv = UNION_DHKE.agree(union_drop_ssk, union_live_spk) s_uv_modulo = s_uv % (2**seed_len) s_uv_entropy = int2bytes(s_uv_modulo, seed_len / 8) union_DRGB_s = HMAC_DRBG(s_uv_entropy, security_strength) union_s_mask = prng(union_DRGB_s, modulo_r_len, security_strength, item_count) sgn = np.sign(client_index_live - client_index_drop) union_sgn_s_mask = sgn * union_s_mask union_sgn_s_mask = union_sgn_s_mask.astype('int64') return union_sgn_s_mask
def server_side_psu_reconstruct_self_mask(UNION_SERVER_STORAGE, UNION_ROUND_STORAGE, union_security_para_dict): """ Reconstruct the random seed b and thus self mask for each live client in U2 using the shares from U3. """ union_all_b_shares = [] for client_index in UNION_ROUND_STORAGE['U3']: union_all_b_shares.append( UNION_SERVER_STORAGE[client_index]['live_b_shares']) union_b_shares_dict = { k: [d.get(k) for d in union_all_b_shares] for k in set().union(*union_all_b_shares) # U2 } # Reconstruct and add up each self mask by PRNG expanding using the seed b # Load parameters for PRNG seed_len = union_security_para_dict['seed_len'] security_strength = union_security_para_dict['security_strength'] modulo_r_len = union_security_para_dict['modulo_r_len'] item_count = union_security_para_dict[ 'item_count'] # length of perturbed Bloom filter (x) # Store sum result union_b_mask_sum = np.zeros(item_count, dtype='int64') # NOT U3!!!!!!! SHOULD BE U2, those clients who send masked input y for client_index in UNION_ROUND_STORAGE['U2']: union_b = SecretSharer.recover_secret( union_b_shares_dict[client_index]) union_b_entropy = int2bytes(union_b, seed_len / 8) union_DRBG_b = HMAC_DRBG(union_b_entropy, security_strength) union_b_mask = prng(union_DRBG_b, modulo_r_len, security_strength, item_count) union_b_mask_sum += union_b_mask return union_b_mask_sum
def psu_reconstruct_single_self_mask(union_b_shares, union_security_para_dict): """ Assistant function for reconstructing self masks in parallel. (Single process) """ # Load parameters for PRNG seed_len = union_security_para_dict['seed_len'] security_strength = union_security_para_dict['security_strength'] modulo_r_len = union_security_para_dict['modulo_r_len'] item_count = union_security_para_dict['item_count'] # length of perturbed Bloom filter (x) union_b = SecretSharer.recover_secret(union_b_shares) union_b_entropy = int2bytes(union_b, seed_len / 8) union_DRBG_b = HMAC_DRBG(union_b_entropy, security_strength) union_b_mask = prng(union_DRBG_b, modulo_r_len, security_strength, item_count) union_b_mask = union_b_mask.astype('int64') return union_b_mask
def server_side_psu_reconstruct_mutual_mask(UNION_SERVER_STORAGE, UNION_ROUND_STORAGE, union_security_para_dict, UNION_DHKE): """ Reconstruct the secret key ssk and thus mutual mask for each dropped client in U1\U2 using the shares from U3. """ item_count = union_security_para_dict[ 'item_count'] # length of perturbed Bloom filter (x) # Store sum result union_s_mask_sum = np.zeros(item_count, dtype='int64') if len(UNION_ROUND_STORAGE['U1\U2'] ) == 0: # Deal with special case no client drops in U2 return union_s_mask_sum union_all_s_shares = [] for client_index in UNION_ROUND_STORAGE['U3']: union_all_s_shares.append( UNION_SERVER_STORAGE[client_index]['drop_s_shares']) union_s_shares_dict = { k: [d.get(k) for d in union_all_s_shares] for k in set().union(*union_all_s_shares) # U1\U2 } # Reconstruct and add up each mutual mask (pair of each dropped client in U1/U2 with each live client in U2) # by PRNG expanding using the secret key ssk # Load parameters for PRNG seed_len = union_security_para_dict['seed_len'] security_strength = union_security_para_dict['security_strength'] modulo_r_len = union_security_para_dict['modulo_r_len'] for client_index_drop in UNION_ROUND_STORAGE['U1\U2']: union_drop_ssk = SecretSharer.recover_secret( union_s_shares_dict[client_index_drop]) # NOT U3!!!!!!! SHOULD BE U2, those clients who send masked input y. for client_index_live in UNION_ROUND_STORAGE['U2']: union_live_spk = UNION_SERVER_STORAGE[client_index_live]['spk'] # Derive seed for mutual mask, i.e., agreed key, (u, v via Diffie-Hellman Agreement) s_uv = UNION_DHKE.agree(union_drop_ssk, union_live_spk) s_uv_modulo = s_uv % (2**seed_len) s_uv_entropy = int2bytes(s_uv_modulo, seed_len / 8) union_DRGB_s = HMAC_DRBG(s_uv_entropy, security_strength) union_s_mask = prng(union_DRGB_s, modulo_r_len, security_strength, item_count) sgn = np.sign(client_index_live - client_index_drop) union_s_mask_sum += sgn * union_s_mask return union_s_mask_sum
def client_side_psu_round1(communication, client_socket, UNION_SELF_STORAGE, UNION_OTHERS_STORAGE, \ union_security_para_dict, UNION_DHKE): """ Generate and send encrypted secret shares for PRNG seed and ssk """ start_time_1 = time.time() # Generate seed for PRNG seed_len = union_security_para_dict['seed_len'] union_b_entropy = os.urandom(seed_len / 8) #bytes union_b = bytes2int(union_b_entropy) t = UNION_SELF_STORAGE['t'] n = UNION_SELF_STORAGE['n'] # Generate t-out-of-n shares for PRNG's seed b union_shares_b = SecretSharer.split_secret(union_b, t, n) # Generate t-out-of-n shares for client's ssk union_shares_my_ssk = SecretSharer.split_secret( UNION_SELF_STORAGE['my_ssk'], t, n) # Store random seed, and secret shares into self dictionary UNION_SELF_STORAGE['b_entropy'] = union_b_entropy ''' UNION_SELF_STORAGE['b'] = union_b UNION_SELF_STORAGE['shares_b'] = union_shares_b UNION_SELF_STORAGE['shares_my_ssk'] = union_shares_my_ssk ''' # Store my share of b in isolation # No need to store my share of my ssk, since I am alive to myself! union_my_share_b = union_shares_b[0] union_shares_b = list(set(union_shares_b) - set([union_my_share_b])) UNION_SELF_STORAGE['my_share_b'] = union_my_share_b union_ss_ciphers_dict = {} for idx, client_index in enumerate(UNION_OTHERS_STORAGE.keys()): # Derive symmetric encryption key "agreed" with other client (with client_index) (via Diffie-Hellman Agreement) sym_enc_key = UNION_DHKE.agree( UNION_SELF_STORAGE['my_csk'], UNION_OTHERS_STORAGE[client_index]['cpk']) # Send ciphertext to other client (with client_index), where PS works as a mediation msg = str(UNION_SELF_STORAGE['my_index']) + ' || ' + str(client_index) + ' || ' + str(union_shares_b[idx]) \ + ' || ' + str(union_shares_my_ssk[idx]) # Encrypt with AES_CBC enc_msg = AESCipher(str(sym_enc_key)).encrypt(msg) union_ss_ciphers_dict[client_index] = enc_msg UNION_OTHERS_STORAGE[client_index]['sym_enc_key'] = sym_enc_key ''' UNION_OTHERS_STORAGE[client_index]['msg'] = msg UNION_OTHERS_STORAGE[client_index]['enc_msg'] = enc_msg ''' end_time_1 = time.time() # send encrypted shares to the server union_ss_ciphers_send_message = { 'client_ID': UNION_SELF_STORAGE['my_index'], 'ss_ciphers': union_ss_ciphers_dict } communication.send_np_array(union_ss_ciphers_send_message, client_socket) print( 'Client %d sent encrypted secret shares to server in private set union' % UNION_SELF_STORAGE['my_index']) sys.stdout.flush() # receive other clients' encrypted shares to me from the server ss_ciphers_dict_received = communication.get_np_array(client_socket) print("Received other clients' encrypted secret shares from server.") sys.stdout.flush() start_time_2 = time.time() for client_index, enc_msg in ss_ciphers_dict_received.items(): # Decrypt the encrypted message and parse it sym_enc_key = UNION_OTHERS_STORAGE[client_index]['sym_enc_key'] msg = AESCipher(str(sym_enc_key)).decrypt(enc_msg) msg_parts = msg.split(' || ') # Sanity check from_client_index = int(msg_parts[0]) my_index = int(msg_parts[1]) assert from_client_index == client_index and my_index == UNION_SELF_STORAGE[ 'my_index'] # Store secret shares of other clients UNION_OTHERS_STORAGE[client_index]['share_b'] = msg_parts[2] UNION_OTHERS_STORAGE[client_index]['share_ssk'] = msg_parts[3] # clients in U1 (except myself) for mutual masks UNION_SELF_STORAGE[ 'mutual_mask_client_indices'] = ss_ciphers_dict_received.keys() end_time_2 = time.time() write_csv(UNION_SELF_STORAGE['client_computation_time_path'], [UNION_SELF_STORAGE['communication_round_number'], \ "psu_U1", end_time_1 - start_time_1 + end_time_2 - start_time_2])
def round3(): U3 = SERVER_VALUES['U3'] n3 = len(U3) print("|U3| the number of clients sending shared masks:", n3) if n3 < SERVER_VALUES['t']: print_failure( 'Did not receive masks and shares from enough clients. Abort.', 'Server') sio.emit( 'abort', 'not enough clients' ) # Broadcast to everyone that the server aborts --> client should disconnect sio.sleep(1) os._exit(-1) # sio.stop() # FIXME # The "dropped out clients" are all the clients sid that were present in the set U2 but not in U3 dropped_out_clients = list(set(SERVER_VALUES['U2']) - set(U3)) SERVER_VALUES['dropped_out_clients_round_3'] = dropped_out_clients Z = 0 print("NB DROPPED OUT CLIENTS =", len(SERVER_VALUES['dropped_out_clients_round_2'])) if SERVER_VALUES['dropped_out_clients_round_2'] != []: # Retrieve the shares of "ssk" of dropped out clients from all alive clients all_ssk_shares = [] for client_sid in U3: all_ssk_shares.append( SERVER_STORAGE[client_sid]['ssk_shares_dropped']) ssk_shares_for_sid = { k: [d.get(k) for d in all_ssk_shares] for k in set().union(*all_ssk_shares) } # Reconstruct ssk from its shares ssk_for_sid = {} for client_sid in SERVER_VALUES['dropped_out_clients_round_2']: ssk = SecretSharer.recover_secret(ssk_shares_for_sid[client_sid]) ssk_for_sid[client_sid] = ssk # Reconstruct all blinding values (s_masks) for all pairs of alive users with the dropped out users all_masks = np.zeros(NB_CLASSES) for dropped_client_sid in SERVER_VALUES['dropped_out_clients_round_2']: for alive_client_sid in U3: s_for_sid = DHKE.agree(ssk_for_sid[dropped_client_sid], SERVER_STORAGE[alive_client_sid]['spk']) np.random.seed(s_for_sid % 2**32) s_mask_for_sid = np.random.uniform(-UNIFORM_S_BOUNDS, UNIFORM_S_BOUNDS, NB_CLASSES) sgn = np.sign( int(alive_client_sid, 16) - int(dropped_client_sid, 16)) all_masks += sgn * s_mask_for_sid Z -= all_masks # Retrieve the shares of "b" from all alive clients all_b_shares = [] for client_sid in U3: all_b_shares.append(SERVER_STORAGE[client_sid]['b_shares_alive']) b_shares_for_sid = { k: [d.get(k) for d in all_b_shares] for k in set().union(*all_b_shares) } # Reconstruct "b" from its shares b_for_sid = {} for client_sid in U3: b = SecretSharer.recover_secret(b_shares_for_sid[client_sid]) b_for_sid[client_sid] = b # Remove b from the y that we received from clients, and aggregate the whole for client_sid in U3: b = b_for_sid[client_sid] np.random.seed(b) b_mask = np.random.uniform(-UNIFORM_B_BOUNDS, UNIFORM_B_BOUNDS, NB_CLASSES) Z += (SERVER_STORAGE[client_sid]['y'] - b_mask) print('Server succesfully aggregate the result and the lenth is ', len(list(Z))) # For Decentralized PATE, the label would be the argmax of this vote vector #print('LABEL:', np.argmax(Z)) SERVER_VALUES['serverround3totalsize'] = total_size({ "aggregation": list(Z), 'U3': U3 }) sio.emit('ROUND_4', {"aggregation": list(Z), 'U3': U3}) #tianjia in 10.11 # Go back to function call (ret) and exit in function timer_round3() above sio.start_background_task(timer_round_4)
def round1(pubkeys): # Store the keys received from the server, in the dictionary CLIENT_STORAGE, for each client_sid for client_sid, pubkeys_for_client_sid in pubkeys.items(): if client_sid == CLIENT_VALUES['my_sid']: continue # Does not need to store my own keys (already stored in CLIENT_VALUES) try: CLIENT_STORAGE.setdefault( client_sid, {})['cpk'] = pubkeys_for_client_sid['cpk'] CLIENT_STORAGE.setdefault( client_sid, {})['spk'] = pubkeys_for_client_sid['spk'] except KeyError: print_failure('Missing key cpk or spk in server' 's messsage.', request.sid) sio.disconnect() # Compute n, the number of active clients (me, included) n = len(CLIENT_STORAGE.keys()) + 1 #; print('n =', n) # Compute t, the minimum number of clients we need for the aggregation t = int(n / 2) + 1 #; print('t =', t) # Draw random seed a, and make a gaussian noise mask out of it a = secrets.randbits(32) #; print('a =', a) # TODO: Chose higher entropy np.random.seed(a) a_noise_vector = np.random.normal( 0, float(SIGMA) / np.sqrt(t), NB_CLASSES) #; print('a_noise_vector =', a_noise_vector) # Draw random seed b, and make a mask out of it b = secrets.randbits(32) #; print('b =', b) np.random.seed(b) b_mask = np.random.uniform( -UNIFORM_B_BOUNDS, UNIFORM_B_BOUNDS, NB_CLASSES ) #; print('b_mask =', b_mask) # TODO: HOW TO CHOOSE THOSE VALUES??? # Create t-out-of-n shares for seed a shares_a = SecretSharer.split_secret(a, t, n) #; print('shares_a =', shares_a) # Create t-out-of-n shares for seed b shares_b = SecretSharer.split_secret(b, t, n) #; print('shares_b =', shares_b) # Create t-out-of-n shares for my private key my_ssk (as an hex_string) shares_my_ssk = SecretSharer.split_secret( CLIENT_VALUES['my_ssk'], t, n) #; print('shares_my_ssk =', shares_my_ssk) # Store all the previously generated values, in client's dictionary CLIENT_VALUES['n'] = n CLIENT_VALUES['t'] = t CLIENT_VALUES['a'] = a CLIENT_VALUES['a_noise'] = a_noise_vector CLIENT_VALUES['b'] = b CLIENT_VALUES['b_mask'] = b_mask CLIENT_VALUES['shares_a'] = shares_a CLIENT_VALUES['shares_b'] = shares_b CLIENT_VALUES['shares_my_ssk'] = shares_my_ssk # Store my share of b in isolation: my_share_b = shares_b[0] shares_b = list(set(shares_b) - set([my_share_b])) CLIENT_VALUES['my_share_b'] = my_share_b list_encrypted_messages = {} for ID, client_sid in enumerate(CLIENT_STORAGE.keys()): if client_sid == CLIENT_VALUES['my_sid']: continue # Skip my own sid # FIXME: Actually, I am NOT part of CLIENT_STORAGE.keys() # Derive encryption key enc_key_for_sid (via Diffie-Hellman Agreement) enc_key_for_sid = DHKE.agree( CLIENT_VALUES['my_csk'], CLIENT_STORAGE[client_sid] ['cpk']) #; print('enc_key_for_sid =', enc_key_for_sid) # Client "client_sid" will be sent this message: msg = 'ProtoV1.0' + ' || ' + str( CLIENT_VALUES['my_sid']) + ' || ' + str(client_sid) + ' || ' + str( shares_my_ssk[ID]) + ' || ' + str(shares_a[ID]) + ' || ' + str( shares_b[ID]) # Encrypt the message with the pre-derived shared encryption key enc_msg = AESCipher(str(enc_key_for_sid)).encrypt(msg) # Store the encrypted messages in a dictionary (keyed by client_sid) that will be sent to the server list_encrypted_messages[client_sid] = enc_msg CLIENT_STORAGE[client_sid]['enc_key'] = enc_key_for_sid CLIENT_STORAGE[client_sid]['msg'] = msg CLIENT_STORAGE[client_sid]['enc_msg'] = enc_msg #print_info('Sending list of encrypted messages to server...', CLIENT_VALUES['my_sid']) sio.emit('ENC_MSGS', list_encrypted_messages, callback=server_ack) if WILL_CRASH: sio.sleep(1) os._exit(0)
def client_side_sfsa_round1(communication, client_socket, FEDSUBAVG_SELF_STORAGE, FEDSUBAVG_OTHERS_STORAGE, \ fedsubavg_security_para_dict, FEDSUBAVG_DHKE): """ Generate and send encrypted secret shares for PRNG seed and ssk. This can be merged with that in private set union, but for clarity, we still do not do so. Different from private set union here is that, the client also receives the indices of other clients for mutual mask. Specifically, we need to handle mutual masks for the embedding layers of item ids and cate ids in a ``submodel" way. """ start_time_1 = time.time() # Generate seed for PRNG seed_len = fedsubavg_security_para_dict['seed_len'] fedsubavg_b_entropy = os.urandom(seed_len / 8) #bytes fedsubavg_b = bytes2int(fedsubavg_b_entropy) t = FEDSUBAVG_SELF_STORAGE['t'] n = FEDSUBAVG_SELF_STORAGE['n'] # Generate t-out-of-n shares for PRNG's seed b fedsubavg_shares_b = SecretSharer.split_secret(fedsubavg_b, t, n) # Generate t-out-of-n shares for client's ssk fedsubavg_shares_my_ssk = SecretSharer.split_secret( FEDSUBAVG_SELF_STORAGE['my_ssk'], t, n) # Store random seed, and secret shares into self dictionary FEDSUBAVG_SELF_STORAGE['b_entropy'] = fedsubavg_b_entropy ''' FEDSUBAVG_SELF_STORAGE['b'] = fedsubavg_b FEDSUBAVG_SELF_STORAGE['shares_b'] = fedsubavg_shares_b FEDSUBAVG_SELF_STORAGE['shares_my_ssk'] = fedsubavg_shares_my_ssk ''' # Store my share of b in isolation # No need to store my share of my ssk, since I am alive to myself! fedsubavg_my_share_b = fedsubavg_shares_b[0] fedsubavg_shares_b = list( set(fedsubavg_shares_b) - set([fedsubavg_my_share_b])) FEDSUBAVG_SELF_STORAGE['my_share_b'] = fedsubavg_my_share_b fedsubavg_ss_ciphers_dict = {} for idx, client_index in enumerate( FEDSUBAVG_OTHERS_STORAGE.keys()): # Already except myself # Derive symmetric encryption key "agreed" with other client (with client_index) (via Diffie-Hellman Agreement) sym_enc_key = FEDSUBAVG_DHKE.agree( FEDSUBAVG_SELF_STORAGE['my_csk'], FEDSUBAVG_OTHERS_STORAGE[client_index]['cpk']) # Send ciphertext to other client (with client_index), where PS works as a mediation msg = str(FEDSUBAVG_SELF_STORAGE['my_index']) + ' || ' + str(client_index) + ' || ' + str(fedsubavg_shares_b[idx]) \ + ' || ' + str(fedsubavg_shares_my_ssk[idx]) # Encrypt with AES_CBC enc_msg = AESCipher(str(sym_enc_key)).encrypt(msg) fedsubavg_ss_ciphers_dict[client_index] = enc_msg FEDSUBAVG_OTHERS_STORAGE[client_index]['sym_enc_key'] = sym_enc_key ''' FEDSUBAVG_OTHERS_STORAGE[client_index]['msg'] = msg FEDSUBAVG_OTHERS_STORAGE[client_index]['enc_msg'] = enc_msg ''' end_time_1 = time.time() # send encrypted shares to the server fedsubavg_ss_ciphers_send_message = { 'client_ID': FEDSUBAVG_SELF_STORAGE['my_index'], 'ss_ciphers': fedsubavg_ss_ciphers_dict } communication.send_np_array(fedsubavg_ss_ciphers_send_message, client_socket) print( 'Client %d sent encrypted secret shares to server in secure federated submodel averaging' % FEDSUBAVG_SELF_STORAGE['my_index']) sys.stdout.flush() # Receive other clients' encrypted shares and indices for mutual mask to me from the server round1_returned_message = communication.get_np_array(client_socket) print( "Received other clients' encrypted secret shares and indices for mutual mask from server" ) sys.stdout.flush() start_time_2 = time.time() # Decrypt the secret shares and store them ss_ciphers_dict_received = round1_returned_message['ss_ciphers_dict'] for client_index, enc_msg in ss_ciphers_dict_received.items(): # Decrypt the encrypted message and parse it sym_enc_key = FEDSUBAVG_OTHERS_STORAGE[client_index]['sym_enc_key'] msg = AESCipher(str(sym_enc_key)).decrypt(enc_msg) msg_parts = msg.split(' || ') # Sanity check from_client_index = int(msg_parts[0]) my_index = int(msg_parts[1]) assert from_client_index == client_index and my_index == FEDSUBAVG_SELF_STORAGE[ 'my_index'] # Store secret shares of other clients FEDSUBAVG_OTHERS_STORAGE[client_index]['share_b'] = msg_parts[2] FEDSUBAVG_OTHERS_STORAGE[client_index]['share_ssk'] = msg_parts[3] # Indices of other clients (except myself) for mutual mask U1\Client Self FEDSUBAVG_SELF_STORAGE[ 'mutual_mask_general_client_indices'] = round1_returned_message[ 'mutual_mask_general_client_indices'] end_time_2 = time.time() write_csv(FEDSUBAVG_SELF_STORAGE['client_computation_time_path'], [FEDSUBAVG_SELF_STORAGE['communication_round_number'], \ "sfsa_U1", end_time_1 - start_time_1 + end_time_2 - start_time_2])
def round1(pubkeys): start = time.time() # Store the keys received from the server, in the dictionary CLIENT_STORAGE, for each client_sid for client_sid, pubkeys_for_client_sid in pubkeys.items(): if client_sid == CLIENT_VALUES['my_sid']: continue # Does not need to store my own keys (already stored in CLIENT_VALUES) try: CLIENT_STORAGE.setdefault(client_sid, {})['cpk'] = pubkeys_for_client_sid['cpk']#当查找的键值 key 不存在的时候,setdefault()函数会返回默认值并更新字典,添加键值 CLIENT_STORAGE.setdefault(client_sid, {})['spk'] = pubkeys_for_client_sid['spk'] except KeyError: print_failure('Missing key cpk or spk in server''s messsage.', client_sid) sio.disconnect() # Compute n, the number of active clients (me, included) n = len(CLIENT_STORAGE.keys()) + 1 #; print('n =', n) # Compute t, the minimum number of clients we need for the aggregation t = int(n/2) + 1 #; print('t =', t) # Draw random seed b, and make a mask out of it b = secrets.randbits(32) #; print('b =', b) np.random.seed(b) b_mask = np.random.uniform(-UNIFORM_B_BOUNDS, UNIFORM_B_BOUNDS, NB_CLASSES) #; print('b_mask =', b_mask) # TODO: HOW TO CHOOSE THOSE VALUES??? # Create t-out-of-n shares for seed b shares_b = SecretSharer.split_secret(b, t, n) #; print('shares_b =', shares_b) # Create t-out-of-n shares for my private key my_ssk (as an hex_string) shares_my_ssk = SecretSharer.split_secret(CLIENT_VALUES['my_ssk'], t, n) #; print('shares_my_ssk =', shares_my_ssk) # Store all the previously generated values, in client's dictionary CLIENT_VALUES['n'] = n; CLIENT_VALUES['t'] = t CLIENT_VALUES['b'] = b; CLIENT_VALUES['b_mask'] = b_mask #CLIENT_VALUES['shares_a'] = shares_a CLIENT_VALUES['shares_b'] = shares_b CLIENT_VALUES['shares_my_ssk'] = shares_my_ssk # Store my share of b in isolation: my_share_b = shares_b[0] shares_b = list( set(shares_b) - set([my_share_b]) ) CLIENT_VALUES['my_share_b'] = my_share_b list_encrypted_messages = {} for ID, client_sid in enumerate(CLIENT_STORAGE.keys()): if client_sid == CLIENT_VALUES['my_sid']: continue # Skip my own sid # FIXME: Actually, I am NOT part of CLIENT_STORAGE.keys() # Derive encryption key enc_key_for_sid (via Diffie-Hellman Agreement) enc_key_for_sid = DHKE.agree(CLIENT_VALUES['my_csk'], CLIENT_STORAGE[client_sid]['cpk']) #; print('enc_key_for_sid =', enc_key_for_sid) # Client "client_sid" will be sent this message: msg = 'ProtoV1.0' + ' || ' + str(CLIENT_VALUES['my_sid']) + ' || ' + str(client_sid) + ' || ' + str(shares_my_ssk[ID]) + ' || ' + str(shares_b[ID]) # Encrypt the message with the pre-derived shared encryption key enc_msg = PrpCrypt(str(enc_key_for_sid)).encrypt(msg) # Store the encrypted messages in a dictionary (keyed by client_sid) that will be sent to the server list_encrypted_messages[client_sid] = enc_msg CLIENT_STORAGE[client_sid]['enc_key'] = enc_key_for_sid CLIENT_STORAGE[client_sid]['msg'] = msg CLIENT_STORAGE[client_sid]['enc_msg'] = enc_msg #Generate Rn************************************* Rn = random.randint(1,1000) CLIENT_VALUES["Rn"] = Rn round1clienttoservermessages = {"Rn":Rn, "enc_msg":list_encrypted_messages} end = time.time() timeofround1 = end - start CLIENT_VALUES['timeofround1'] = timeofround1 clientround1totalsize = total_size(round1clienttoservermessages) CLIENT_VALUES['clientround1totalsize'] = clientround1totalsize print("timeofround1:",timeofround1,"clientround1totalsize:",clientround1totalsize) sio.emit('ENC_MSGS', round1clienttoservermessages, callback=server_ack) # drop out in round 1 set(U1) - set(U0) if WILL_CRASH: sio.sleep(1) os._exit(0)
def round3(): U3 = SERVER_VALUES['U3'] n3 = len(U3) if n3 < SERVER_VALUES['t']: print_failure( 'Did not receive masks and shares from enough clients. Abort.', 'Server') sio.emit( 'abort', 'not enough clients' ) # Broadcast to everyone that the server aborts --> client should disconnect sio.sleep(1) os._exit(-1) # sio.stop() # FIXME # The "dropped out clients" are all the clients sid that were present in the set U2 but not in U3 dropped_out_clients = list(set(SERVER_VALUES['U2']) - set(U3)) SERVER_VALUES['dropped_out_clients_round_3'] = dropped_out_clients Z = 0 print("NB DROPPED OUT CLIENTS =", len(SERVER_VALUES['dropped_out_clients_round_2'])) if SERVER_VALUES['dropped_out_clients_round_2'] != []: # Retrieve the shares of "ssk" of dropped out clients from all alive clients all_ssk_shares = [] for client_sid in U3: all_ssk_shares.append( SERVER_STORAGE[client_sid]['ssk_shares_dropped']) ssk_shares_for_sid = { k: [d.get(k) for d in all_ssk_shares] for k in set().union(*all_ssk_shares) } # Reconstruct ssk from its shares ssk_for_sid = {} for client_sid in SERVER_VALUES['dropped_out_clients_round_2']: ssk = SecretSharer.recover_secret(ssk_shares_for_sid[client_sid]) ssk_for_sid[client_sid] = ssk # Reconstruct all blinding values (s_masks) for all pairs of alive users with the dropped out users all_masks = np.zeros(NB_CLASSES) for dropped_client_sid in SERVER_VALUES['dropped_out_clients_round_2']: for alive_client_sid in U3: s_for_sid = DHKE.agree(ssk_for_sid[dropped_client_sid], SERVER_STORAGE[alive_client_sid]['spk']) np.random.seed(s_for_sid % 2**32) s_mask_for_sid = np.random.uniform(-UNIFORM_S_BOUNDS, UNIFORM_S_BOUNDS, NB_CLASSES) sgn = np.sign( int(alive_client_sid, 16) - int(dropped_client_sid, 16)) all_masks += sgn * s_mask_for_sid Z -= all_masks # Retrieve the shares of "b" from all alive clients all_b_shares = [] for client_sid in U3: all_b_shares.append(SERVER_STORAGE[client_sid]['b_shares_alive']) b_shares_for_sid = { k: [d.get(k) for d in all_b_shares] for k in set().union(*all_b_shares) } # Reconstruct "b" from its shares b_for_sid = {} for client_sid in U3: b = SecretSharer.recover_secret(b_shares_for_sid[client_sid]) b_for_sid[client_sid] = b # Remove b from the y that we received from clients, and aggregate the whole for client_sid in U3: b = b_for_sid[client_sid] np.random.seed(b) b_mask = np.random.uniform(-UNIFORM_B_BOUNDS, UNIFORM_B_BOUNDS, NB_CLASSES) Z += (SERVER_STORAGE[client_sid]['y'] - b_mask) # More noise that neccesary (t), remove that extra noise if 'extra_noises' in SERVER_VALUES: extra_noises = np.array(SERVER_VALUES['extra_noises']) extra_noises_sum = np.sum(extra_noises, axis=0) Z -= extra_noises_sum # Z is now the approriately noised array, containing the aggregation of private # vectors of the still alive clients # print('Z = ', Z) print('[*]', time.time(), 'Done') # For Decentralized PATE, the label would be the argmax of this vote vector #print('LABEL:', np.argmax(Z)) sio.emit('complete', 'Reconstructed output z!')