示例#1
0
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)
# shares = SecretSharer.split_secret(sk, 5, 6)
# # for s in shares:
# #     print('-', s)
# #     print('LENGTH:', len(str(s)))
# # print()
#
# secretTrue = SecretSharer.recover_secret(shares[0:5])
# print(secretTrue == sk)
#
# secretFalse = SecretSharer.recover_secret(shares[0:4]) # not enough
# print(secretFalse == sk)

sk1, pk1 = DHKE.generate_keys()
sk2, pk2 = DHKE.generate_keys()

shared_key = DHKE.agree(sk1, pk2)
shared_key2 = DHKE.agree(sk2, pk1)

assert shared_key == shared_key2

# TODO: Use KDF to generate AES key

sender = OT_Sender('Dentifrice', 'Laboratoire')
receiver = OT_Receiver(1)

A = sender.generate_A()
# print('A =', A)
B = receiver.generate_B(A)
# print('B =', B)
e0, e1 = sender.generate_e0_e1(B)
# print('e0 =', e0)
示例#3
0
def round2(enc_msgs):

    for client_sid, enc_msg in enc_msgs.items():

        # Decrypt the encrypted message and parse it
        enc_key_for_sid = CLIENT_STORAGE[client_sid]['enc_key']
        msg = AESCipher(str(enc_key_for_sid)).decrypt(enc_msg)

        msg_parts = msg.split(' || ')

        protocol_id = msg_parts[0]  # TODO: What's the use? #TODO: Timestamp?
        from_client_sid = msg_parts[1]
        my_sid = msg_parts[2]
        share_ssk_for_sid = msg_parts[3]
        share_a_for_sid = msg_parts[4]
        share_b_for_sid = msg_parts[5]

        # Store has been received for client_sid
        CLIENT_STORAGE[from_client_sid]['share_ssk'] = share_ssk_for_sid
        CLIENT_STORAGE[from_client_sid]['share_a'] = share_a_for_sid
        CLIENT_STORAGE[from_client_sid]['share_b'] = share_b_for_sid

        # Sanity check
        if client_sid != from_client_sid or my_sid != CLIENT_VALUES['my_sid']:
            print_failure('Received wrong message!', CLIENT_VALUES['my_sid'])
            sio.disconnect()

        # Derive secret shared mask seed s_for_sid (Diffie-Hellman Agreement)
        s_for_sid = DHKE.agree(CLIENT_VALUES['my_ssk'],
                               CLIENT_STORAGE[client_sid]
                               ['spk'])  #; print('s_for_sid =', s_for_sid)

        # Derive s_mask from above seed
        np.random.seed(
            s_for_sid %
            2**32)  # TODO: Higher entropy than 2**32??? (max value to .seed())
        s_mask_for_sid = np.random.uniform(
            -UNIFORM_S_BOUNDS, UNIFORM_S_BOUNDS, NB_CLASSES
        )  #; print('s_for_sid =', s_for_sid )# TODO: Which values??

        # Store also that
        CLIENT_STORAGE[client_sid]['s'] = s_for_sid
        CLIENT_STORAGE[client_sid]['s_mask'] = s_mask_for_sid

    # Construct masked input:
    # First the noisy input (the one that the server will aggregate)
    noisy_x = CLIENT_VALUES['x'] + CLIENT_VALUES['a_noise']

    # Then, add the individual mask
    yy = noisy_x + CLIENT_VALUES['b_mask']

    all_masks = np.zeros(NB_CLASSES)
    for client_sid in CLIENT_STORAGE.keys():
        if client_sid == CLIENT_VALUES['my_sid']:
            continue  # Skip my own SID
        if not 's_mask' in CLIENT_STORAGE[client_sid].keys():
            print_failure("No shared mask for client", client_sid)
            continue  # We do not have shared mask from this client SID
        sgn = np.sign(
            int(CLIENT_VALUES['my_sid'], 16) -
            int(client_sid, 16))  # Substract the masks of greater client SIDs,
        all_masks += sgn * CLIENT_STORAGE[client_sid][
            's_mask']  # or add those of smaller client SIDs

    # Here is the final output "y" to send to server
    y = yy + all_masks

    #print_info('Sending masked input "y" to server...', CLIENT_VALUES['my_sid'])
    sio.emit(
        'INPUT_Y', list(y), callback=server_ack
    )  # Send "y" as a python list because numpy arrays are not JSON-serializable
示例#4
0
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)
示例#5
0
def round2(round1servertoclientmessages):
    #print("round1servertoclientmessages:",round1servertoclientmessages) success 10.10
    
    for client_sid, enc_msg in round1servertoclientmessages["enc_msgs"].items():

        # Decrypt the encrypted message and parse it
        enc_key_for_sid = CLIENT_STORAGE[client_sid]['enc_key']
        msg = PrpCrypt(str(enc_key_for_sid)).decrypt(enc_msg)

        msg_parts = msg.split(' || ')

        protocol_id = msg_parts[0] # TODO: What's the use? #TODO: Timestamp?
        from_client_sid = msg_parts[1]
        my_sid = msg_parts[2]
        share_ssk_for_sid = msg_parts[3]
        share_b_for_sid = msg_parts[4]

        # Store has been received for client_sid
        CLIENT_STORAGE[from_client_sid]['share_ssk'] = share_ssk_for_sid
        #CLIENT_STORAGE[from_client_sid]['share_a'] = share_a_for_sid
        CLIENT_STORAGE[from_client_sid]['share_b'] = share_b_for_sid

        # Sanity check
        if client_sid != from_client_sid or my_sid != CLIENT_VALUES['my_sid']:
            print_failure('Received wrong message!', CLIENT_VALUES['my_sid'])
            sio.disconnect()

        # Derive secret shared mask seed s_for_sid (Diffie-Hellman Agreement)
        s_for_sid = DHKE.agree(CLIENT_VALUES['my_ssk'], CLIENT_STORAGE[client_sid]['spk'])         #; print('s_for_sid =', s_for_sid)

        # Derive s_mask from above seed
        np.random.seed(s_for_sid % 2**32) # TODO: Higher entropy than 2**32??? (max value to .seed())
        s_mask_for_sid = np.random.uniform(-UNIFORM_S_BOUNDS, UNIFORM_S_BOUNDS, NB_CLASSES)                                  #; print('s_for_sid =', s_for_sid )# TODO: Which values??

        # Store also that
        CLIENT_STORAGE[client_sid]['s'] = s_for_sid
        CLIENT_STORAGE[client_sid]['s_mask'] = s_mask_for_sid


    # Construct masked input:
    yy = CLIENT_VALUES['x'] + CLIENT_VALUES['b_mask']

    #the second masked in our paper
    all_masks = np.zeros(NB_CLASSES)
    for client_sid in CLIENT_STORAGE.keys():
        if client_sid == CLIENT_VALUES['my_sid']:
            continue # Skip my own SID
        if not 's_mask' in CLIENT_STORAGE[client_sid].keys():
            print_failure("No shared mask for client", client_sid)
            continue # We do not have shared mask from this client SID
        sgn = np.sign(int(CLIENT_VALUES['my_sid'], 16) - int(client_sid, 16))  # Substract the masks of greater client SIDs,
        all_masks += sgn * CLIENT_STORAGE[client_sid]['s_mask']                # or add those of smaller client SIDs

    # Here is the final output "y" to send to server
    y = yy + all_masks

    '''
    Identity-Based Aggregate Signatures
    ->round0 & round 2 
    data:10.10
    '''
    '''individual signing step 2'''
    start = time.time()
    #extract sessionID from the message received from the server
    sessionID = round1servertoclientmessages['SID']
    #print("client receive SID",sessionID)
    
    #store the sessionID

    CLIENT_VALUES["sessionID"] = sessionID

    #calculate w by sessionID
    sha1 = hashlib.sha1()
    sha1.update(str(sessionID).encode("utf-8"))#str(sessionID) list ->string
    w = str(sha1.hexdigest())
    #w = "w" test first  
    #print("w->",w)

    P_n0 = CLIENT_VALUES['P_n0']
    P_n1 = CLIENT_VALUES['P_n1']
    sP_n0 = CLIENT_VALUES['sP_n0']
    sP_n1 = CLIENT_VALUES['sP_n1']

    Un = CLIENT_VALUES['my_sid']
    k_n = CLIENT_VALUES['k_n']
    Kn = CLIENT_VALUES['Kn']

    #VElGamal encryption
    x_n = []
    for i in range(NB_CLASSES):
        x_n.append(int(CLIENT_VALUES['x'][i]*1e6))

    #print("x_n",x_n)

    Cn = veg.enc(x_n,k_n)

    #construct Mn
    Mn= str(Kn)+str(Cn)+str(sessionID)
    sigma_n = ibas.InSign(w,Mn,Un,sP_n0,sP_n1)
    #print("Individual signature:", sigma_n)
    c_n = Element.from_hash(pairing, Zr, Un + Mn + w)

    #print("c_n",c_n,type(c_n))
    #print("P_n0",P_n0,type(P_n0))
    #print("P_n1",P_n1,type(P_n1))

    strsigma_n = [sigma_n[0],str(sigma_n[1]),str(sigma_n[2])] #shoud be convert to pypbc.Element.G2
    strc_n = str(c_n)#in fact it should by reconstruct though little computation cost
    strP_n0 = str(P_n0)
    strP_n1 = str(P_n1)

    round2clienttoservermessages = {"mask_x":list(y),"sigma_n":strsigma_n,"Cn":Cn,"Kn":Kn,"c_n":strc_n,"P_n0":strP_n0,"P_n1":strP_n1,'w':w}

    #print_info('Sending masked input "y" to server...', CLIENT_VALUES['my_sid'])
    end = time.time()
    timeofround2 = end - start
    CLIENT_VALUES['timeofround2'] = timeofround2
    clientround2totalsize = total_size(round2clienttoservermessages)
    CLIENT_VALUES['clientround2totalsize'] = clientround2totalsize

    print("timeofround2:",timeofround2,"clientround2totalsize:",clientround2totalsize)
    sio.emit('INPUT_Y', round2clienttoservermessages, callback=server_ack) # Send "y" as a python list because numpy arrays are not JSON-serializable
示例#6
0
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)
示例#7
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!')