示例#1
0
def key_request(IP: str, port: int) -> Optional[RSA_key]:
    """Request the TTP's public key.
    
    PARAMETERS
    ==========
    IP: A string containing the IP address of the TTP.
    port: The port the TTP is listening on, as an integer.

    RETURNS
    =======
    On success, return an RSA_key object. If there was a communications error,
      return None.
    """
    sock = create_socket(IP, port)
    if sock is None:
        return None
    count = send(sock, b'k')
    if count != 1:
        return close_sock(sock)
    ttp_N = receive(sock, 128)
    if len(ttp_N) != 128:
        return close_sock(sock)
    ttp_d = receive(sock, 128)
    if len(ttp_d) != 128:
        return close_sock(sock)

    close_sock(sock)
    return RSA_key(pubkey=(bytes_to_int(ttp_N), bytes_to_int(ttp_d)))
def key_request(IP: str, port: int) -> Optional[RSA_key]:
    """Request the TTP's public key.
    
    PARAMETERS
    ==========
    IP: A string containing the IP address of the TTP.
    port: The port the TTP is listening on, as an integer.

    RETURNS
    =======
    On success, return an RSA_key object. If there was a communications error,
      return None.
    """
    sock = create_socket(IP, port)
    k_byte = 'k'.encode('utf-8')
    sentlen = send(sock, k_byte)
    if sentlen != 1:
        sock.close()
        return None
    ttp_n = receive(sock, 128)
    ttp_d = receive(sock, 128)
    if (len(ttp_d) != 128) or len(ttp_n) != 128:
        sock.close()
        return None
    sock.close()
    rsakey = RSA_key(pubkey=(bytes_to_int(ttp_n), bytes_to_int(ttp_d)))
    return rsakey
def sign_request( IP: str, port: int, server_name: str, server_key: RSA_key ) -> \
        Optional[tuple[int,int]]:
    """Sign the server's public key, via the TTP.
    
    PARAMETERS
    ==========
    IP: A string containing the IP address of the TTP.
    port: The port the TTP is listening on, as an integer.
    server_name: The server's name, as a string.
    server_key: The server's RSA key, as an RSA_key object.

    RETURNS
    =======
    On success, return (ttp_N, ttp_sig) as integers; the former is the TTP's
       RSA modulus, the latter the TTP's signature of the public key. If the 
       TTP could not be contacted, or any other error occurred, return None.
    """
    sock = create_socket(IP, port)
    if sock == None:
        return None

    sentlen = send(sock, b's')
    if sentlen != 1:
        sock.close()
        return None

    name = server_name.encode('utf-8')
    nameLength = len(name)
    nameLengthByte = int_to_bytes(nameLength, 1)
    sentlen = send(sock, nameLengthByte)
    if sentlen != 1:
        sock.close()
        return None

    sentlen = send(sock, name)
    if sentlen != len(name):
        sock.close()
        return None

    if server_key.N == None or server_key.e == None:
        sock.close()
        return None
    N_bytes = int_to_bytes(server_key.N, 128)
    sentlen = send(sock, N_bytes)
    if sentlen != 128:
        sock.close()
        return None
    e_bytes = int_to_bytes(server_key.e, 128)
    sentlen = send(sock, e_bytes)
    if sentlen != 128:
        sock.close()
        return None
    ttp_n = receive(sock, 128)
    ttp_sig = receive(sock, 128)
    if (len(ttp_sig) != 128) or len(ttp_n) != 128:
        sock.close()
        return None
    return (bytes_to_int(ttp_n), bytes_to_int(ttp_sig))
示例#4
0
def sign_request( IP: str, port: int, server_name: str, server_key: RSA_key ) -> \
        Optional[tuple[int,int]]:
    """Sign the server's public key, via the TTP.
    
    PARAMETERS
    ==========
    IP: A string containing the IP address of the TTP.
    port: The port the TTP is listening on, as an integer.
    server_name: The server's name, as a string.
    server_key: The server's RSA key, as an RSA_key object.

    RETURNS
    =======
    On success, return (ttp_N, ttp_sig) as integers; the former is the TTP's
       RSA modulus, the latter the TTP's signature of the public key. If the 
       TTP could not be contacted, or any other error occurred, return None.
    """

    sock = create_socket(IP, port)
    if sock is None:
        return None

    count = send(sock, b's')
    if count != 1:
        return close_sock(sock)

    server_name_length = len(server_name.encode())
    server_name_length_bytes = int_to_bytes(server_name_length, 1)

    count = send(sock, server_name_length_bytes)
    if count != 1:
        return close_sock(sock)

    count = send(sock, server_name.encode())
    if count != server_name_length:
        return close_sock(sock)

    N_bytes = int_to_bytes(server_key.N, 128)
    count = send(sock, N_bytes)
    if count != 128:
        return close_sock(sock)

    e_bytes = int_to_bytes(server_key.e, 128)
    count = send(sock, e_bytes)
    if count != 128:
        return close_sock(sock)

    ttp_N = receive(sock, 128)
    if len(ttp_N) != 128:
        return close_sock(sock)

    ttp_sig = receive(sock, 128)
    if len(ttp_sig) != 128:
        return close_sock(sock)

    close_sock(sock)
    return (bytes_to_int(ttp_N), bytes_to_int(ttp_sig))
示例#5
0
    def decrypt(self, cypher: Union[int, bytes]) -> Union[int, None]:
        """Decrypt a message via this RSA key.
    
        PARAMETERS
        ==========
        cypher: The encrypted message. Could be an int or bytes object.

        RETURNS
        =======
        The decrypted value, as an integer, if this contains a private
           key. Otherwise, returns None.

        EXAMPLES
        ========
        > plain = server_key.decrypt( crypt )
        """

        assert type(cypher) in [int, bytes]
        if (type(cypher) == bytes):
            cypher = bytes_to_int(cypher)
        #decrypt with private key to decrypt
        if self.d:
            return pow(cypher, self.d, self.N)
        else:
            return None
示例#6
0
    def sign(self, message: Union[int, bytes]) -> Union[int, None]:
        """Sign a message via this RSA key, if possible.
    
        PARAMETERS
        ==========
        message: The message to be signed. Could be an int or bytes object.

        RETURNS
        =======
        If this has a private key, return the signature as an integer value.
           If it does not, return None.

        EXAMPLES
        ========
        > key = RSA_key()
        > sig = key.sign( 42 )
        """

        assert type(message) in [int, bytes]
        if (type(message) == bytes):
            message = bytes_to_int(message)
        #encrypt with private key to sign
        if self.d:
            return pow(message, self.d, self.N)
        else:
            return None
    def sign(self, message: Union[int, bytes]) -> Union[int, None]:
        """Sign a message via this RSA key, if possible.
    
        PARAMETERS
        ==========
        message: The message to be signed. Could be an int or bytes object.

        RETURNS
        =======
        If this has a private key, return the signature as an integer value.
           If it does not, return None.

        EXAMPLES
        ========
        > key = RSA_key()
        > sig = key.sign( 42 )
        """

        assert type(message) in [int, bytes]
        if self.d == None:
            return None
        else:
            if isinstance(message, bytes):
                Message = bytes_to_int(message)
            else:
                Message = message
            print(pow(Message, self.d, self.N))
            print("\n****************\n")
            return pow(Message, self.d, self.N)
示例#8
0
    def encrypt(self, message: Union[int, bytes]) -> int:
        """Encrypt a message via this RSA key.
    
        PARAMETERS
        ==========
        message: The message to be encrypted. Could be an int or bytes object.

        RETURNS
        =======
        The encrypted value, as an integer.

        EXAMPLES
        ========
        > key    = RSA_key()
        > cypher = key.encrypt( 42 )
        """

        assert type(message) in [int, bytes]
        if (type(message) == bytes):
            message = bytes_to_int(message)

        #encrypt with public key to encrypt
        return pow(message, self.e, self.N)
示例#9
0
def client_protocol( ip: str, port: int, dh: DH_params, ttp_key: RSA_key, \
        username: str, pw: str, s: bytes, file_bytes: bytes ) -> \
        Optional[tuple[int,int]]:
    """Generate the shared key and send the file, from the client side.
       IMPORTANT: don't forget to send 'p'!

    PARAMETERS
    ==========
    ip: The IP address to connect to, as a string.
    port: The port to connect to, as an int.
    dh: A DH_params object.
    ttp_key: An RSA_key object.
    username: The username to register, as a string.
    pw: The password, as a string.
    s: The salt, a bytes object 16 bytes long. Must match what the server sends
       back.
    file_bytes: The plaintext to send to the server, as a bytes object.

    RETURNS
    =======
    If successful, return a tuple of the form (a, K_client), where both a and
       K_client are integers. If not, return None.
    """
    sock = create_socket(ip, port)
    if sock is None:
        return None

    count = send(sock, b'p')
    if count != 1:
        return close_sock(sock)

    encoded_username = username.encode()
    encoded_username_length = len(encoded_username)

    count = send(sock, int_to_bytes(encoded_username_length, 1))
    if count != 1:
        return close_sock(sock)

    count = send(sock, encoded_username)
    if count != encoded_username_length:
        return close_sock(sock)

    encoded_server_name_length_bytes = receive(sock, 1)
    if len(encoded_server_name_length_bytes) != 1:
        return close_sock(sock)
    encoded_server_name_length = bytes_to_int(encoded_server_name_length_bytes)

    server_cert = receive(sock, (encoded_server_name_length + 384))
    if len(server_cert) != (encoded_server_name_length + 384):
        return close_sock(sock)

    ttp_sig_rec = server_cert[-128:]
    server_cert = server_cert[:-128]

    server_name_bytes = server_cert[:-256]
    server_N_bytes = server_cert[-256:-128]
    server_e_bytes = server_cert[-128:]

    server_N = bytes_to_int(server_N_bytes)
    server_e = bytes_to_int(server_e_bytes)
    server_name = server_name_bytes.decode()
    server_key = RSA_key(pubkey=(server_N, server_e))

    digest = hashes.Hash(hashes.SHA3_512())
    digest.update(server_cert)
    t = digest.finalize()

    digest = hashes.Hash(hashes.SHA3_512())
    digest.update(t)
    t_prime = digest.finalize()

    S = bytes_to_int(t + t_prime) - ttp_key.N

    ttp_sig = ttp_key.sign(S)

    if ttp_sig_rec != ttp_sig:
        return close_sock(sock)

    #COPIED FROM A2 BELOW
    a = int.from_bytes(os.urandom(63), byteorder="big")

    A = calc_A(dh.N, dh.g, a)
    #A = int_to_bytes(A, 64)
    enc_A = server_key.encrypt(A)
    enc_A_bytes = int_to_bytes(A, 128)

    send(sock, enc_A_bytes)  #3
    print("Client: A = " + str(bytes_to_int(A)))
    s = receive(sock, 16)  #4
    print("Client: Recieved " + s.hex())
    B = receive(sock, 64)  #5
    print("Client: Recieved " + s.hex())

    u = calc_u(A, B)
    x = calc_x(s, pw)
    v = calc_A(dh.N, dh.g, x)

    #where is k? I already calculated it.......
    byteN = dh.N
    byteG = dh.g
    if type(byteN) == int:
        byteN = int_to_bytes(byteN, 64)
    if type(byteG) == int:
        byteG = int_to_bytes(byteG, 64)
    byteK = hash_bytes(byteN + byteG)
    k = int.from_bytes(byteK, "big")

    K_client = calc_K_client(N, B, k, v, a, u, x)

    M1 = calc_M1(A, B, K_client)
    send(sock, M1)  #6
    print("Client: M1 = " + M1.hex())

    M2 = receive(sock, 32)  #7
    print("Client: Recieved " + M2.hex())
    M2check = calc_M2(A, M1, K_client)
    if M2 != M2check:
        return close_sock(sock)

    K_client_bytes = int_to_bytes(K_client, 64)
    AES_key = K_client_bytes[:32]
    HMAC_key = K_client_bytes[32:]
    encrypted_file_bytes = pad_encrypt_then_HMAC(
        file_bytes, AES_key, HMAC_key
    )  #this might now work, might need to break up file and only pad final block

    encrypted_file_bytes_length = len(encrypted_file_bytes)
    encrypted_file_bytes_length_bytes = int_to_bytes(
        encrypted_file_bytes_length, 4)

    count = send(sock, encrypted_file_bytes_length_bytes)
    if count != 4:
        return close_sock(sock)

    count = send(sock, encrypted_file_bytes)
    if count != encrypted_file_bytes_length:
        return close_sock(sock)

    close_sock(sock)
    return (a, K_client)
示例#10
0
def server_protocol( sock: socket.socket, dh: DH_params, server_key: RSA_key, \
        server_name: str, ttp_sig: int, database: Mapping[str,tuple[bytes,int]] ) -> \
        Optional[ tuple[str,int,bytes,bytes,bytes] ]:
    """Carry out the protocol and file transfer as per the assignment.
       IMPORTANT: 'p' has already been sent!
    
    PARAMETERS
    ==========
    sock: A socket connected to the client.
    dh: A DH_params object.
    server_key: An RSA_key object. 
    server_name: The server's name, as a string.
    ttp_sig: The signature returned by the TTP, as an int.
    database: A dict containing the user database, as per A2.

    RETURNS
    =======
    If the protocol was successful, return the tuple ( username, b, 
       AES_key, HMAC_key, plaintext ), which are (in order) the username
       supplied by the client, as a string; the server's randomly-chosen value
       for b, as an integer; the key used to encrypt the file transfer, as a
       bytes object; the key used for message authentication, as a bytes
       object; and the plaintext version of the file, as a bytes object.
       If the protocol failed, return None.
    """
    encoded_username_length = receive(sock, 1)
    if len(encoded_username_length) != 1:
        return close_sock(sock)

    username_length = bytes_to_int(encoded_username_length)

    encoded_username = receive(sock, username_length)
    if len(encoded_username) != username_length:
        return close_sock(sock)

    username = encoded_username.decode()

    encoded_server_name = server_name.encode()
    encoded_server_name_length = len(encoded_server_name)

    count = send(sock, int_to_bytes(encoded_server_name_length, 1))
    if count != 1:
        return close_sock(sock)

    data = encoded_server_name + int_to_bytes(server_key.N, 128) + int_to_bytes(server_key.e, 128) \
        + int_to_bytes(ttp_sig, 128)
    count = send(sock, data)
    if count != (384 + encoded_server_name_length):
        return close_sock(sock)

    #copied from Assignment 2 Below
    b = int.from_bytes(os.urandom(63), byteorder="big")

    enc_A_bytes = receive(sock, 128)  #3
    print("Server: Recieved " + enc_A_bytes.hex())
    enc_A = bytes_to_int(enc_A_bytes)
    A = server_key.decrypt(enc_A)
    if A % dh.N == 0:
        return close_sock(sock)
    if username in database:
        s, v = database[username]
    else:
        return close_sock(sock)

    #where is k? I already calculated it.......
    byteN = dh.N
    byteG = dh.g
    if type(byteN) == int:
        byteN = int_to_bytes(byteN, 64)
    if type(byteG) == int:
        byteG = int_to_bytes(byteG, 64)
    byteK = hash_bytes(byteN + byteG)
    k = int.from_bytes(byteK, "big")

    B = calc_B(dh.N, dh.g, b, k, v)
    send(sock, bytes(s))  #4
    print("Server: salt = " + s.hex())
    send(sock, int_to_bytes(B, 64))  #5
    print("Server: B = " + str(B))

    u = calc_u(A, B)

    K_server = calc_K_server(dh.N, A, b, v, u)

    M1 = receive(sock, 32)  #6
    print("Server: Recieved " + M1.hex())
    M1check = calc_M1(A, B, K_server)
    if M1 != M1check:
        return close_sock(sock)
    M2 = calc_M2(A, M1, K_server)
    send(sock, M2)  #7
    print("Server: M2 = " + M2.hex())

    encrypted_file_bytes_length_bytes = receive(sock, 4)
    if len(encrypted_file_bytes_length_bytes) != 4:
        return close_sock(sock)
    encrypted_file_bytes_length = bytes_to_int(
        encrypted_file_bytes_length_bytes)

    encrypted_file_bytes = receive(sock, encrypted_file_bytes_length)
    if len(encrypted_file_bytes) != encrypted_file_bytes_length:
        return close_sock(sock)

    K_server_bytes = int_to_bytes(K_server, 64)
    AES_key = K_server_bytes[:32]
    HMAC_key = K_server_bytes[32:]

    decrypted_file = decrypt_and_verify(encrypted_file_bytes, AES_key,
                                        HMAC_key)
    if decrypted_file == None:
        return close_sock(sock)

    close_sock(sock)
    return (username, b, decrypted_file, AES_key, HMAC_key)
示例#11
0
def ttp_sign( sock: socket.socket, ttp_key: RSA_key, \
        database: Mapping[str,RSA_key]  ) -> Optional[Mapping[str,RSA_key]]:
    """Carry out the TTP's signing procedure. IMPORTANT: 's' has already
       been read!
    
    PARAMETERS
    ==========
    sock: The communication socket to send/receive data over. Must be closed
       before the function exits.
    ttp_key: An RSA_key object.
    database: A dictionary of all signatures generated, of the form 
        database[server_name] = key, where server_name is a string and
        key is an RSA_key object.

    RETURNS
    =======
    If the server has not requested a signature before, and the values can be 
       signed, return an updated version of the database. If the server has 
       already requested a signature but with different information, return None. 
       If the information was the same, return the database unmodified. If a
       socket error occurs, return None.
    """

    assert type(sock) is socket.socket
    assert type(database) == dict

    length_name = receive(sock, 1)
    if len(length_name) != 1:
        return close_sock(sock)
    length_name = bytes_to_int(length_name)
    varprint(length_name, 'length_name', "TTP")

    name = receive(sock, length_name)
    if len(name) != length_name:
        return close_sock(sock)
    varprint(name.decode("utf-8"), 'name', "name")

    serv_N = receive(sock, 128)
    if len(serv_N) != 128:
        return close_sock(sock)

    serv_e = receive(sock, 128)
    if len(serv_e) != 128:
        return close_sock(sock)

    serv_key = (bytes_to_int(serv_N), bytes_to_int(serv_e))
    varprint(serv_key, 'serv_key', "TTP")

    db_index = name.decode("utf-8")
    if db_index in database:
        db_serv_key = database[db_index]
        if db_serv_key.N != serv_key[0] or db_serv_key.e != serv_key[1]:
            print("Server name exists with different information. Quitting...")
            return close_sock(sock)

    digest = hashes.Hash(hashes.SHA3_512())
    digest.update(name + serv_N + serv_e)
    t = digest.finalize()

    digest = hashes.Hash(hashes.SHA3_512())
    digest.update(t)
    t_prime = digest.finalize()

    S = bytes_to_int(t + t_prime) - ttp_key.N

    ttp_sig = ttp_key.sign(S)

    N_bytes = int_to_bytes(ttp_key.N, byte_length_of_int(ttp_key.N))
    count = send(sock, N_bytes)
    if count != len(N_bytes):
        return close_sock(sock)

    ttp_sig_bytes = int_to_bytes(ttp_sig, byte_length_of_int(ttp_sig))
    count = send(sock, ttp_sig_bytes)
    if count != len(ttp_sig_bytes):
        return close_sock(sock)

    if db_index not in database:
        database[db_index] = RSA_key(pubkey=serv_key)

    close_sock(sock)
    return database
def client_protocol( ip: str, port: int, dh: DH_params, ttp_key: RSA_key, \
        username: str, pw: str, s: bytes, file_bytes: bytes ) -> \
        Optional[tuple[int,int]]:
    """Generate the shared key and send the file, from the client side.
       IMPORTANT: don't forget to send 'p'!

    PARAMETERS
    ==========
    ip: The IP address to connect to, as a string.
    port: The port to connect to, as an int.
    dh: A DH_params object.
    ttp_key: An RSA_key object.
    username: The username to register, as a string.
    pw: The password, as a string.
    s: The salt, a bytes object 16 bytes long. Must match what the server sends
       back.
    file_bytes: The plaintext to send to the server, as a bytes object.

    RETURNS
    =======
    If successful, return a tuple of the form (a, K_client), where both a and
       K_client are integers. If not, return None.
    """
    try:
        sock = create_socket(ip, port)
        # if sock==None:
        #     return None
        p = 'p'.encode('utf-8')
        send(sock, p)
        usernameUTF = username.encode('utf-8')
        send(sock, int_to_bytes(len(usernameUTF), 1))
        send(sock, usernameUTF)

        serverNameLen1 = receive(sock, 1)
        serverNameLen = bytes_to_int(serverNameLen1)
        serverNameBytes = receive(sock, serverNameLen)
        serverName = serverNameBytes.decode('utf-8')
        servN_bytes = receive(sock, 128)
        serve_bytes = receive(sock, 128)

        servN = bytes_to_int(servN_bytes)
        serve = bytes_to_int(serve_bytes)
        server_key = RSA_key(pubkey=(servN, serve))

        ttpSig_bytes = receive(sock, 128)
        NameNe = serverNameBytes + servN_bytes + serve_bytes
        ttpSign = bytes_to_int(ttpSig_bytes)
        digest = hashes.Hash(hashes.SHA3_512())
        digest.update(NameNe)
        t = digest.finalize()
        digest = hashes.Hash(hashes.SHA3_512())
        digest.update(t)
        tdash = digest.finalize()
        tFinal = bytes_to_int(t + tdash)
        verSig = pow(tFinal, ttp_key.d, ttp_key.N)

        # verify
        if (verSig != ttpSign):
            sock.close()
            return None
        N = dh.N
        g = dh.g
        a = random.randint(0, N - 1)
        A = calc_A(N, g, a)
        encA = server_key.encrypt(A)
        encABytes = int_to_bytes(encA, 128)
        sentLen = send(sock, encABytes)

        salt = receive(sock, 16)

        if salt != s:
            sock.close()
            return None
        B = receive(sock, 64)
        # calc u
        u = calc_u(A, B)

        k = calc_u(N, g)

        # calc x
        x = calc_x(s, pw)

        # calc v
        v = pow(g, x, N)
        # calc K_client
        k_client = calc_K_client(N, B, k, v, a, u, x)
        # calc M1 and send
        M1 = calc_M1(A, B, k_client)
        sentLen = send(sock, M1)
        if sentLen < len(M1):
            sock.close()
            return None

        M2 = receive(sock, 32)
        clientM2 = calc_M2(A, M1, k_client)
        if (M2 == clientM2):
            k_client_bytes = int_to_bytes(k_client, 64)
            aesKey = k_client_bytes[:32]
            hmacKey = k_client_bytes[32:]
            cyphertext = pad_encrypt_then_HMAC(file_bytes, aesKey, hmacKey)
            cypherLen = int_to_bytes(len(cyphertext), 4)
            send(sock, cypherLen)
            send(sock, cyphertext)
            sock.close()
            return (A, k_client)
        sock.close()
        return None
    except:
        return None
def server_protocol( sock: socket.socket, dh: DH_params, server_key: RSA_key, \
        server_name: str, ttp_sig: int, database: Mapping[str,tuple[bytes,int]] ) -> \
        Optional[ tuple[str,int,bytes,bytes,bytes] ]:
    """Carry out the protocol and file transfer as per the assignment.
       IMPORTANT: 'p' has already been sent!
    
    PARAMETERS
    ==========
    sock: A socket connected to the client.
    dh: A DH_params object.
    server_key: An RSA_key object. 
    server_name: The server's name, as a string.
    ttp_sig: The signature returned by the TTP, as an int.
    database: A dict containing the user database, as per A2.

    RETURNS
    =======
    If the protocol was successful, return the tuple ( username, b, 
       AES_key, HMAC_key, plaintext ), which are (in order) the username
       supplied by the client, as a string; the server's randomly-chosen value
       for b, as an integer; the key used to encrypt the file transfer, as a
       bytes object; the key used for message authentication, as a bytes
       object; and the plaintext version of the file, as a bytes object.
       If the protocol failed, return None.
    """

    clientUsernameLength = receive(sock, 1)

    clientUsernameUTF = receive(sock, bytes_to_int(clientUsernameLength))

    clientUsername = clientUsernameUTF.decode("utf-8")

    server_nameUTF = server_name.encode("utf-8")
    server_nameLen = len(server_nameUTF)
    sentLen = send(sock, int_to_bytes(server_nameLen, 1))

    sentLen = send(sock, server_nameUTF)
    Nbytes = int_to_bytes(server_key.N, 128)
    ebytes = int_to_bytes(server_key.e, 128)
    ttpsignbytes = int_to_bytes(ttp_sig, 128)
    sentLen = send(sock, Nbytes)
    sentLen = send(sock, ebytes)
    sentLen = send(sock, ttpsignbytes)

    encA = receive(sock, 128)
    A = server_key.decrypt(encA)
    if (A % dh.N == 0):
        sock.close()
        return None
    A_bytes = int_to_bytes(A, 64)
    N = dh.N
    g = dh.g
    k = calc_u(N, g)
    reply = database.get(clientUsername)
    if reply == None:
        database = ttp_sign(sock, server_key, database)
    reply = database.get(clientUsername)
    s = reply[0]
    v = reply[1]
    b = random.randint(0, N - 1)
    B = calc_B(N, g, b, k, v)
    B_bytes = int_to_bytes(B, 64)

    concat = s + B_bytes
    lenSent = send(sock, concat)
    '''s and b sent'''

    u = calc_u(A_bytes, B_bytes)
    k_server = calc_K_server(N, A_bytes, b, v, u)
    k_server_bytes = int_to_bytes(k_server, 64)
    M1 = receive(sock, 32)
    m1serv = calc_M1(A_bytes, B_bytes, k_server)
    M2 = calc_M2(A, M1, k_server)
    lenSent = send(sock, M2)

    if (M1 == m1serv):

        aesKey = k_server_bytes[:32]
        hmacKey = k_server_bytes[32:]

        cyphertextLen = receive(sock, 4)
        cypher = receive(sock, bytes_to_int(cyphertextLen))
        plaintext = decrypt_and_verify(cypher, aesKey, hmacKey)
        sock.close()
        if plaintext == None:
            return None
        return (clientUsername, b, aesKey, hmacKey, plaintext)
    else:
        sock.close()
        return None
def ttp_sign( sock: socket.socket, ttp_key: RSA_key, \
        database: Mapping[str,RSA_key]  ) -> Optional[Mapping[str,RSA_key]]:
    """Carry out the TTP's signing procedure. IMPORTANT: 's' has already
       been read!
    
    PARAMETERS
    ==========
    sock: The communication socket to send/receive data over. Must be closed
       before the function exits.
    ttp_key: An RSA_key object.
    database: A dictionary of all signatures generated, of the form 
        database[server_name] = key, where server_name is a string and
        key is an RSA_key object.

    RETURNS
    =======
    If the server has not requested a signature before, and the values can be 
       signed, return an updated version of the database. If the server has 
       already requested a signature but with different information, return None. 
       If the information was the same, return the database unmodified. If a
       socket error occurs, return None.
    """

    assert type(sock) is socket.socket
    assert type(database) == dict
    nameLengthBytes = receive(sock, 1)

    nameLength = bytes_to_int(nameLengthBytes)
    nameBytes = receive(sock, nameLength)
    N_bytes = receive(sock, 128)
    e_bytes = receive(sock, 128)
    if (len(N_bytes) != 128) or (len(e_bytes) != 128):
        sock.close()
        return None
    hash = hashes.Hash(hashes.SHA3_512())
    hash.update(nameBytes + N_bytes + e_bytes)
    t = hash.finalize()
    hash1 = hashes.Hash(hashes.SHA3_512())
    hash1.update(t)
    tDash = hash1.finalize()
    tAndtdash = bytes_to_int(t + tDash)
    N = bytes_to_int(N_bytes)
    e = bytes_to_int(e_bytes)
    S = tAndtdash % ttp_key.N
    if database.get(nameBytes.decode('utf-8')) == None:
        key = RSA_key(pubkey=(N, e))
        sig = ttp_key.sign(S)
        database[nameBytes.decode('utf-8')] = key

        newNbytes = int_to_bytes(ttp_key.N, 128)
        if (sig == None):
            sock.close()
            return database
        sentLength = send(sock, newNbytes)
        if sentLength != 128:
            sock.close()
            return database

        sig_bytes = int_to_bytes(sig, 128)
        sentLength = send(sock, sig_bytes)
        if sentLength != 256:
            sock.close()
            return database
        sock.close()
        return database
    else:
        key = database[nameBytes.decode('utf-8')]
        if key.N == N and key.e == e:
            #send(sock,N_bytes)
            sig = ttp_key.sign(S)
            newNbytes = int_to_bytes(ttp_key.N, 128)
            if (sig == None):
                sock.close()
                return database
            sentLength = send(sock, newNbytes)
            if sentLength != 128:
                sock.close()
                return database

            sig_bytes = int_to_bytes(sig, 128)
            sentLength = send(sock, sig_bytes)
            if sentLength != 256:
                sock.close()
                return database
            sock.close()
            return database

        sock.close()
        return None