def ping_check(username, password, target_connection_address, my_active_usernames=None): """ Checks if another client is active Return: data_object - object """ url = 'http://{}/api/ping_check'.format(target_connection_address) my_time = time.time() host = cherrypy.config.get('server.socket_host') port = cherrypy.config.get('server.socket_port') connection_address = '{}:{}'.format(host, port) connection_location = '2' headers = api_helper.create_header(username, password) if my_active_usernames != None: payload = { 'my_time': my_time, 'my_active_usernames': my_active_usernames, 'connection_address': connection_address, 'connection_location': connection_location, } else: payload = { 'my_time': my_time, 'connection_address': connection_address, 'connection_location': connection_location, } json_bytes = json.dumps(payload).encode('utf-8') data_object = api_helper.get_data(url, headers=headers, data=json_bytes) return data_object
def group_invite(username, password, target_username): """ Transmits a secret group message between users """ url = 'http://172.23.159.9:1025/api/rx_groupinvite' # uni loginserver_record = 'wyao332,69592f14f52422ecf713b21f1615da2fec7d67eb7f0a8c4d3a72121d8e49cb66,1559114951.7035556,d0a5992d76f5f5464ddc0a530d8ea5f8a99b0fde4e0a3d4b91d100b7515188929ef22801420f25cc0b0f51095fa8cd9fbe6d3c93e1a93b7b2857cafdd6159a0e' groupkey_hash = '69592f14f52422ecf713b21f1615da2fec7d67eb7f0a8c4d3a72121d8e49cb66' pubkey = '69592f14f52422ecf713b21f1615da2fec7d67eb7f0a8c4d3a72121d8e49cb66' encrypted_groupkey = pubkey ts = '1559114951.7035556' keys = security_helper.get_keys( loginserver_record + groupkey_hash + pubkey + username + encrypted_groupkey + str(ts)) # FOR TESTING PURPOSES headers = api_helper.create_header(username, password) payload = { 'loginserver_record': loginserver_record, 'groupkey_hash': groupkey_hash, 'target_pubkey': pubkey, 'target_username': username, 'encrypted_groupkey': pubkey, 'sender_created_at': str(ts), 'signature': keys['signature'], } json_bytes = json.dumps(payload).encode('utf-8') data_object = api_helper.get_data(url, headers=headers, data=json_bytes) data_object = json.loads(data_object) return data_object
def add_pubkey(username, password): """ Associates a public key with the user's account Return: data_object - object """ url = "http://cs302.kiwi.land/api/add_pubkey" # generate new private key prikey = security_helper.generate_private_key() # upload private key to privatedata private_data = get_privatedata(username, password) (private_data['prikeys'])[0] = prikey add_privatedata(username, password, private_data) pubkey = security_helper.get_public_key(prikey) signature = security_helper.get_signature(prikey, pubkey, username=username) headers = api_helper.create_header(username, password) payload = { 'pubkey': pubkey, 'username': username, 'signature': signature, } json_bytes = json.dumps(payload).encode('utf-8') data_object = api_helper.get_data(url, headers=headers, data=json_bytes) return data_object
def report_user_status(username, password, status='online'): """ Informs the login server about connection information for the user Return: data_object - Python object """ url = 'http://cs302.kiwi.land/api/report' host = cherrypy.config.get('server.socket_host') port = cherrypy.config.get('server.socket_port') connection_address = '{}:{}'.format(host, port) connection_location = '2' prikey = get_privatekey(username, password) pubkey = security_helper.get_public_key(prikey) headers = api_helper.create_header(username, password) payload = { 'connection_address': connection_address, 'connection_location': connection_location, 'incoming_pubkey': pubkey, 'status': status, } json_bytes = json.dumps(payload).encode('utf-8') data_object = api_helper.get_data(url, headers=headers, data=json_bytes) return data_object
def check_pubkey(username, password, pubkey): """ Loads the loginserver_record for a given public key Return: data_object - object """ url = "http://cs302.kiwi.land/api/check_pubkey?pubkey={}".format(pubkey) headers = api_helper.create_header(username, password) data_object = api_helper.get_data(url, headers=headers) return data_object
def load_new_apikey(username, password): """ Returns a new API key for authentication for the rest of the session Note: implemented for future use Return: data_object - object """ url = 'http://cs302.kiwi.land/api/load_new_apikey' headers = api_helper.create_header(username, password) data_object = api_helper.get_data(url, headers=headers) return data_object
def get_loginserver_record(username, password): """ Loads the user's current loginserver_record for use in creating point-to-point messages. Return: data_object - object """ url = 'http://cs302.kiwi.land/api/get_loginserver_record' headers = api_helper.create_header(username, password) data_object = api_helper.get_data(url, headers=headers) return data_object
def list_users(username, password): """ Lists the connection details for all active users within the last five minutes Return: data_object - Python object """ url = 'http://cs302.kiwi.land/api/list_users' headers = api_helper.create_header(username, password) data_object = api_helper.get_data(url, headers=headers) if data_object['response'] == 'ok': return data_object else: return {'response': 'error', 'message': data_object['message']}
def add_privatedata(username, password, data): """ Saves symmetrically encrypted private data for a user Return: data_object - object """ url = 'http://cs302.kiwi.land/api/add_privatedata' key = 'strongkey' # FOR TESTING PURPOSE: change to take user input # data = { # 'prikeys': ['cd7f971fc826eeb354c5ade4293b5e83a93c74c1aa624a2c28e6a14b97ae3d0d'], # 'blocked_pubkeys': [], # 'blocked_usernames': [], # 'blocked_words': [], # 'blocked_message_signatures': [], # 'favourite_message_signatures': [], # 'friends_usernames': [], # } json_data = json.dumps(data) encrypted_data = security_helper.encrypt_data(key, json_data) loginserver_record = get_loginserver_record(username, password) ts = time.time() prikey = get_privatekey(username, password) pubkey = security_helper.get_public_key(prikey) message_data = encrypted_data + loginserver_record + str(ts) signature = security_helper.get_signature(prikey, pubkey, message_data=message_data) headers = api_helper.create_header(username, password) payload = { 'privatedata': encrypted_data, 'loginserver_record': loginserver_record, 'client_saved_at': str(ts), 'signature': signature, } json_bytes = json.dumps(payload).encode('utf-8') data_object = api_helper.get_data(url, headers=headers, data=json_bytes) return data_object
def ping(username, password): """ Checks if the login server is online and authenticates login Return: data_object - object """ url = 'http://cs302.kiwi.land/api/ping' prikey = get_privatekey(username, password) pubkey = security_helper.get_public_key(prikey) signature = security_helper.get_signature(prikey, pubkey) headers = api_helper.create_header(username, password) payload = { "pubkey": pubkey, "signature": signature, } json_bytes = json.dumps(payload).encode('utf-8') data_object = api_helper.get_data(url, headers=headers, data=json_bytes) return data_object
def get_privatedata(username, password): """ Loads the saved symmetrically encrypted private data of the user Return: privatedata - decrypted data as Python object Or if error, Python object with error message """ url = 'http://cs302.kiwi.land/api/get_privatedata' key = 'strongkey' # FOR TESTING PURPOSE: change to take user input headers = api_helper.create_header(username, password) data_object = api_helper.get_data(url, headers=headers) if data_object['response'] == 'ok': encrypted_data = data_object['privatedata'] decrypted_data = security_helper.decrypt_data(key, encrypted_data) privatedata = json.loads(decrypted_data) return privatedata else: return {'response': 'error', 'message': data_object['message']}
def private_message(username, password, target_username, message): """ Transmits a secret message between users. """ # sender details loginserver_record = login_server.get_loginserver_record(username, password)[ 'loginserver_record'] ts = time.time() # receiver details target_pubkey = user_repository.get_pubkey(target_username) isOnline = False # finds connection address of receiver connection_address = None users = login_server.list_users(username, password)['users'] for user in users: if user['username'] == target_username: cherrypy.log('{} is online'.format(target_username)) isOnline = True connection_address = user['connection_address'] if target_pubkey != user['incoming_pubkey']: target_pubkey = user['incoming_pubkey'] user_repository.post_user_info(target_username, target_pubkey) cherrypy.log('updated user\'s pubkey') break if target_pubkey != None: # encrypt message encrypted_message = security_helper.encrypt_message(target_pubkey, message) prikey = login_server.get_privatekey(username, password) pubkey = security_helper.get_public_key(prikey) message_data = loginserver_record + \ target_pubkey + username + message + str(ts) signature = security_helper.get_signature( prikey, pubkey, message_data=message_data) headers = api_helper.create_header(username, password) payload = { 'loginserver_record': loginserver_record, 'target_pubkey': target_pubkey, 'target_username': target_username, 'encrypted_message': encrypted_message, 'sender_created_at': str(ts), 'signature': signature, } json_bytes = json.dumps(payload).encode('utf-8') # send message pingFailed = False if isOnline: # ping receiver to check if available response = ping_check(username, password, connection_address) if response['response'] != 'ok': cherrypy.log('{}: Ping error: {}'.format( connection_address, response['message'])) pingFailed = True if not pingFailed: # send to receiver url = 'http://{}/api/rx_privatemessage'.format(connection_address) data_object = api_helper.get_data( url, headers=headers, data=json_bytes) cherrypy.log('Private message sent (recipient)') # send to everyone else if not isOnline or pingFailed: for user in users: response = ping_check(username, password, user['connection_address']) try: if response['response'] != 'ok': cherrypy.log('{}: Ping error: {}'.format( user['connection_address'], response['message'])) data_object = { 'response': 'error', 'message': 'Ping error' } else: url = 'http://{}/api/rx_privatemessage'.format( user['connection_address']) data_object = api_helper.get_data( url, headers=headers, data=json_bytes) data_object = api_helper.get_data( url, headers=headers, data=json_bytes) cherrypy.log('{}: {}'.format( user['connection_address'], data_object['response'])) except TypeError: continue except KeyError: continue cherrypy.log('Private message sent (everyone)') else: cherrypy.log('No target pubkey') data_object = { 'response': 'error', 'message': 'No pubkey for target user' } return data_object
def check_messages(username, password): """ Retrieve already-sent messages from other clients in the network Return: data_object - Python object """ headers = api_helper.create_header(username, password) # get time user was last online login_times = user_repository.get_login_times(username) last_online = login_times[-2][0] # broadcast to everyone that's online users = login_server.list_users(username, password)['users'] # users = [{'connection_address': '127.0.0.1:1025'}] for user in users: connection_address = user['connection_address'] # ping client to check if they are online response = ping_check(username, password, connection_address) try: if response['response'] != 'ok': cherrypy.log('{}: Ping error: {}'.format( connection_address, response['message'])) continue except KeyError: continue except TypeError: continue url = 'http://{}/api/checkmessages?since={}'.format( connection_address, last_online) data_object = api_helper.get_data( url, headers=headers) try: if data_object['response'] == 'ok': broadcasts = data_object['broadcasts'] private_messages = data_object['private_messages'] # post new messages to database for b in broadcasts: broadcast_repository.post_broadcast( b['loginserver_record'], b['message'], b['sender_created_at'], b['signature']) for p in private_messages: private_message_repository.post_message( p['loginserver_record'], p['target_pubkey'], p['target_username'], p['encrypted_message'], p['sender_created_at'], p['signature']) cherrypy.log('{}: {}'.format( connection_address, data_object['response'])) else: cherrypy.log('{}: {}'.format( connection_address, data_object['message'])) except KeyError: continue except TypeError: continue except json.decoder.JSONDecodeError: continue return data_object
def broadcast(username, password, message): """ Transmits a signed broadcast between users Returns: 'ok' - string """ loginserver_record = login_server.get_loginserver_record(username, password)[ 'loginserver_record'] ts = time.time() prikey = login_server.get_privatekey(username, password) pubkey = security_helper.get_public_key(prikey) message_data = loginserver_record + message + str(ts) signature = security_helper.get_signature( prikey, pubkey, message_data=message_data) headers = api_helper.create_header(username, password) payload = { 'loginserver_record': loginserver_record, 'message': message, 'sender_created_at': str(ts), 'signature': signature, } json_bytes = json.dumps(payload).encode('utf-8') # broadcast to everyone that's online users = login_server.list_users(username, password)['users'] for user in users: connection_address = user['connection_address'] # ping client to check if they are online response = ping_check(username, password, connection_address) try: if response['response'] != 'ok': cherrypy.log('{}: Ping error: {}'.format( connection_address, response['message'])) continue except KeyError: continue except TypeError: continue url = 'http://{}/api/rx_broadcast'.format(connection_address) data_object = api_helper.get_data( url, headers=headers, data=json_bytes) try: if data_object['response'] == 'ok': cherrypy.log('{}: {}'.format( connection_address, data_object['response'])) else: cherrypy.log('{}: {}'.format( connection_address, data_object['message'])) except TypeError: continue except KeyError: continue cherrypy.log('Broadcast sent') return 'ok'