def handle_incoming_message_thread(recv): conn, addr = recv client_packet = proto.ClientPacket() try: client_packet.ParseFromString(conn.recv(TCP_BUFFER_SIZE)) message_request = client_packet.messageRequest sender, message = get_decrypted_message(message_request) print("Sender: ", sender, " says:", message) message_response = proto.ClientPacket() message_response.messageResponse.success = True conn.send(message_response.SerializeToString()) return except google.protobuf.message.DecodeError: print("Received an invalid proto!!")
def send_message_thread(message_args): global me global PIECE_SIZE if not logged_in: print("Please login first!!") return part_counter = 0 receiver, message = message_args.split(" ", 1) receiver.replace(" ", "") message.strip() # Fetch userlist for freshness, maybe someone logged out. if not server_list_thread(True): print( "Unable to check freshness of the user, hoping for the best and sending a message!!" ) if not chat_users.has_key(receiver): print("User", receiver, "not registered with the server!!") return client_packet = get_encrypted_message(receiver, message) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Let's just assume our message is smaller than TCP_BUFFER_SIZE for now. sock.connect(chat_users[receiver]) sock.send(client_packet.SerializeToString()) message_response = proto.ClientPacket() message_response.ParseFromString(sock.recv(TCP_BUFFER_SIZE)) if message_response.messageResponse.success: print("Message sent!!") else: print("Message send failure!!") sock.close()
def signup_thread(args): if ip == None or port == None: print( "Server information not provided!! Please set server info with connect command or check help." ) return username, password = args.split(" ", 1) username.replace(" ", "") # No spaces in username. password.replace(" ", "") # No spaces in password. tls_state = setup_tls_with_server() if tls_state == None: return server_request = proto.ServerPacket() server_request.signupRequest.username = username server_request.signupRequest.password = password send_tls_packet(tls_state, server_request) client_packet = proto.ClientPacket() decrypted_packet = get_tls_packet(tls_state) if decrypted_packet == None: return client_packet.ParseFromString(decrypted_packet) server_signup_response = client_packet.signupResponse if server_signup_response.success: print("User", username, " signed up successfully!!") else: print("User", username, " signup FAILURE!!") tls_state[0].close()
def logout_thread(): global username global password global logged_in if not logged_in: print("Please be logged in to logout :P") return logged_in = False tls_state = setup_tls_with_server() if tls_state == None: return server_request = proto.ServerPacket() server_request.logoutRequest.username = username server_request.logoutRequest.password = password send_tls_packet(tls_state, server_request) client_packet = proto.ClientPacket() decrypted_packet = get_tls_packet(tls_state) if decrypted_packet == None: return client_packet.ParseFromString(decrypted_packet) server_logout_response = client_packet.logoutResponse if server_logout_response.success: print("Signout successful!!") else: print("Signout failure!!") tls_state[0].close()
def get_encrypted_message(dst, message): lookup = False ephemeral_key = None # Lookup state with dst if not kdf_sending_chain.has_key(dst): # Need to fetch identity key and prekey from server. prekey_response = fetch_prekey(dst) ik_dst = serialization.load_pem_public_key(str( prekey_response.identityKey), backend=default_backend()) dh1 = session_identity_key.exchange(ec.ECDH(), ik_dst) opk_dst = serialization.load_pem_public_key(str( prekey_response.preKey), backend=default_backend()) ephemeral_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) dh2 = ephemeral_key.exchange(ec.ECDH(), opk_dst) # chain_position = 0 kdf_sending_chain[dst] = (dh1 + dh2, 0) lookup = True # Now we have a KDF chain for the sender! kdf_checkpoint = kdf_sending_chain[dst] secret, chain_position = kdf_sending_chain[dst] # Gen ck(n+1) hasher = hmac.HMAC(secret, hashes.SHA256(), backend=default_backend()) hasher.update(hex(0)) hmac0 = hasher.finalize() hasher = hmac.HMAC(secret, hashes.SHA256(), backend=default_backend()) hasher.update(hex(1)) hmac1 = hasher.finalize() derived_random = HKDF( algorithm=hashes.SHA256(), length=80, # 32 message key, 32 hmac key, 16 IV salt=None, info=None, backend=default_backend()).derive(hmac1) symmetric_key_ephemeral = derived_random[0:32] hmac_key = derived_random[32:64] iv = derived_random[64:80] ct, cth = encrypt_and_hash(symmetric_key_ephemeral, hmac_key, iv, message) # Update KDF chain for next message. kdf_sending_chain[dst] = (hmac0, chain_position + 1) client_packet = proto.ClientPacket() serialized_identity_public_key = session_identity_key.public_key( ).public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) client_packet.messageRequest.identityKey = serialized_identity_public_key if ephemeral_key != None: serialized_ephemeral_key = ephemeral_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) client_packet.messageRequest.senderEphemeral = serialized_ephemeral_key client_packet.messageRequest.preKey = prekey_response.preKey client_packet.messageRequest.chainPosition = chain_position client_packet.messageRequest.payload = ct client_packet.messageRequest.oneDoesNotSimplyMAC = cth return client_packet
def login_thread(username, password): global listen_port global session_identity_key global pre_keys global logged_in session_identity_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) serialized_identity_public_key = session_identity_key.public_key( ).public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) server_packet = proto.ServerPacket() server_packet.loginRequest.username = username server_packet.loginRequest.password = password server_packet.loginRequest.identityKey = serialized_identity_public_key # Add N oneTimePreKeys. for i in range(PREKEY_SIZE): pk = ec.generate_private_key(ec.SECP384R1(), default_backend()) pk_pub = pk.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo) pre_keys.append(pk) server_packet.loginRequest.oneTimePreKeys.append(pk_pub) # Wait for listen port information while listen_port == 0: time.sleep(1) server_packet.loginRequest.port = listen_port tls_state = setup_tls_with_server() if tls_state == None: return send_tls_packet(tls_state, server_packet) try: data = get_tls_packet(tls_state) if data == None: print("Failed to register with Server!! Please try again...") return client_packet = proto.ClientPacket() client_packet.ParseFromString(data) if (client_packet.loginResponse.acknowledge): print("Registered with Server!") global registered registered = True tls_state[0].close() else: print("Received response from Server ACK was unset!!") except socket.timeout: print("Failed to register with Server!! Please try again...") logged_in = False tls_state[0].close() except google.protobuf.message.DecodeError: print("Received an invalid proto as response!! Please try again...") logged_in = False tls_state[0].close() logged_in = True tls_state[0].close() server_list_thread(True)
def handle_signup(signupRequest, tls_state): # Storing password in a dictionary, not written to disk, so any comprimise of # RAM is as bad as comprimising a salted hash, if we were to use such a system. print "Signup request:", signupRequest.username success = True if not password_store.has_key(signupRequest.username): password_store[signupRequest.username] = signupRequest.password else: success = False client_packet = proto.ClientPacket() client_packet.signupResponse.success = success send_tls_packet(tls_state, client_packet)
def handle_list_thread(listRequest, tls_state): global registered_users print "List request!!" client_packet = proto.ClientPacket() i = 0 connectedUsers = client_packet.listResponse.connectedUsers for user, details in registered_users.iteritems(): connectedUsers.add() connectedUsers[i].username = user connectedUsers[i].userIP = "TEEHEE" connectedUsers[i].listenPort = 0 i = i + 1 send_tls_packet(tls_state, client_packet)
def handle_identity_request(identityRequest, tls_state): success = False client_packet = proto.ClientPacket() response = client_packet.identityResponse for user, ik in identity_key_store.items(): if ik == identityRequest.identityKey: response.identityKey = ik response.username = user response.userIP, response.port = registered_users[user] success = True break response.success = success send_tls_packet(tls_state, client_packet)
def handle_prekey_request(prekeyRequest, tls_state): success = False client_packet = proto.ClientPacket() response = client_packet.prekeyResponse if registered_users.has_key(prekeyRequest.username): success = True response.username = prekeyRequest.username response.identityKey = identity_key_store[prekeyRequest.username] response.preKey = pre_key_store[prekeyRequest.username].pop() response.userIP, response.port = registered_users[ prekeyRequest.username] response.success = success send_tls_packet(tls_state, client_packet)
def handle_logout(logoutRequest, tls_state): if not password_store.has_key(logoutRequest.username): print "Unidentified username!!" return if password_store[logoutRequest.username] != logoutRequest.password: print "Unauthorized logout for user:"******"using", logoutRequest.password return if not registered_users.has_key(logoutRequest.username): print "User", logoutRequest.username, "already logged out!!" return # delete things stored at login. del registered_users[logoutRequest.username] del identity_key_store[logoutRequest.username] del pre_key_store[logoutRequest.username] print "Successful logout!! Sending response..." client_packet = proto.ClientPacket() client_packet.logoutResponse.success = True send_tls_packet(tls_state, client_packet)
def handle_login(loginRequest, addr, tls_state): if not password_store.has_key(loginRequest.username): print "Unidentified username!!" return if password_store[loginRequest.username] != loginRequest.password: print "Unauthorized login for user:"******"using", loginRequest.password return print "New connection from:", loginRequest.username, "listening at:", loginRequest.port registered_users[loginRequest.username] = (addr[0], loginRequest.port) identity_key_store[loginRequest.username] = loginRequest.identityKey pre_key_store[loginRequest.username] = [] for prekey in loginRequest.oneTimePreKeys: pre_key_store[loginRequest.username].append(prekey) print "Got identity key:", identity_key_store[loginRequest.username] print "Got", len(pre_key_store[loginRequest.username]), "prekeys" client_packet = proto.ClientPacket() client_packet.loginResponse.acknowledge = True send_tls_packet(tls_state, client_packet)
def fetch_prekey(username): tls_state = setup_tls_with_server() if tls_state == None: return server_request = proto.ServerPacket() username.replace(" ", "") server_request.prekeyRequest.username = username send_tls_packet(tls_state, server_request) client_packet = proto.ClientPacket() decrypted_packet = get_tls_packet(tls_state) if decrypted_packet == None: return client_packet.ParseFromString(decrypted_packet) prekey_response = client_packet.prekeyResponse if prekey_response.success: print("Successful fetch!!") chat_users[prekey_response.username] = (prekey_response.userIP, prekey_response.port) else: print("Failed fetching prekey for:", username) tls_state[0].close() return prekey_response
def fetch_identity(identity_pubkey): tls_state = setup_tls_with_server() if tls_state == None: return server_request = proto.ServerPacket() identity_pubkey.replace(" ", "") server_request.identityRequest.identityKey = identity_pubkey send_tls_packet(tls_state, server_request) client_packet = proto.ClientPacket() decrypted_packet = get_tls_packet(tls_state) if decrypted_packet == None: return client_packet.ParseFromString(decrypted_packet) identity_response = client_packet.identityResponse if identity_response.success: print("Successful identity lookup!!") visited = [] for ik, user in identity_keys.items(): if user == identity_response.username: if ik != identity_response.identityKey: # Identity key updated, delete old kdf chain. del kdf_sending_chain[identity_response.username] del kdf_receiving_chain[identity_response.username] visited.append(ik) else: # We just fetched an IK for a user we already had, this shouldn't be possible. print("Weird pkf, but ok.") for ik in visited: # Delete old ik associated to this identity. del identity_keys[ik] # Save the new ik to this username identity_keys[ identity_response.identityKey] = identity_response.username chat_users[identity_response.username] = (identity_response.userIP, identity_response.port) else: print("Failed to fetch identity for:", identity_pubkey) tls_state[0].close() return identity_response
def server_list_thread(quiet): global chat_users server_packet = proto.ServerPacket() server_packet.listRequest.askForList = True tls_state = setup_tls_with_server() if tls_state == None: return send_tls_packet(tls_state, server_packet) try: data = get_tls_packet(tls_state) if data == None: tls_state[0].close() return False client_packet = proto.ClientPacket() client_packet.ParseFromString(data) users_active = [] for user in client_packet.listResponse.connectedUsers: users_active.append(user.username) if not chat_users.has_key(user.username): chat_users[user.username] = () for u in set(chat_users.keys()) - set(users_active): del chat_users[u] if not quiet: print("Users registered:", chat_users.keys()) except socket.timeout: if not quiet: print("Failed to fetch list!! Please try again...") tls_state[0].close() return False except google.protobuf.message.DecodeError: if not quiet: print( "Received an invalid proto as response!! Please try again...") tls_state[0].close() return False tls_state[0].close()