Server working
This commit is contained in:
52
client/api.py
Normal file
52
client/api.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import requests
|
||||
|
||||
class APIError(Exception):
|
||||
"A custom error we'll use when something goes wrong with the API"
|
||||
|
||||
class SubRosaAPI:
|
||||
"""An API for the SubRosa server.
|
||||
"""
|
||||
|
||||
def __init__(self, url):
|
||||
self.url = url
|
||||
|
||||
def get_user(self, username):
|
||||
route = "/users"
|
||||
params = {'name': username}
|
||||
return self.get(route, params)
|
||||
|
||||
def create_user(self, username, public_key):
|
||||
route = "/users/new"
|
||||
params = {'name': username, 'public_key': public_key}
|
||||
return self.post(route, params)
|
||||
|
||||
def get_messages(self, username):
|
||||
route = "/messages"
|
||||
params = {'name': username}
|
||||
return self.get(route, params)
|
||||
|
||||
def send_message(self, sender, recipient, ciphertext, time_sent, time_sent_signature):
|
||||
route = "/messages/send"
|
||||
params = {
|
||||
'sender': sender,
|
||||
'recipient': recipient,
|
||||
'ciphertext': ciphertext,
|
||||
'time_sent': time_sent,
|
||||
'time_sent_signature': time_sent_signature,
|
||||
}
|
||||
return self.get(route, params)
|
||||
|
||||
def get(self, route, params):
|
||||
response = requests.get(self.url + route, json=params)
|
||||
if response.ok:
|
||||
return response.json()
|
||||
else:
|
||||
raise APIError(response.json()['error'])
|
||||
|
||||
def post(self, route, params):
|
||||
response = requests.post(self.url + route, json=params)
|
||||
if response.ok:
|
||||
return response.json()
|
||||
else:
|
||||
raise APIError(response.json()['error'])
|
||||
|
106
client/user_interface.py
Normal file
106
client/user_interface.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from pathlib import Path
|
||||
from api import SubRosaAPI, APIError
|
||||
from datetime import datetime
|
||||
import sys
|
||||
sys.path.insert(0, ".")
|
||||
from encryption import PrivateKey, PublicKey
|
||||
|
||||
class SubRosaUI:
|
||||
"""A user interface to the SubRosa chat system.
|
||||
"""
|
||||
def __init__(self, private_key_file, url):
|
||||
self.api = SubRosaAPI(url)
|
||||
if Path(private_key_file).exists():
|
||||
self.private_key = PrivateKey.load(private_key_file)
|
||||
else:
|
||||
self.private_key = PrivateKey.generate()
|
||||
self.private_key.save(private_key_file)
|
||||
self.public_key = self.private_key.get_public_key()
|
||||
|
||||
def run(self, username):
|
||||
self.username = username
|
||||
self.user = self.get_or_create_user(username)
|
||||
print("Welcome to SubRosa.")
|
||||
while True:
|
||||
choice = self.choose_action_from_menu()
|
||||
if choice == 'VIEW':
|
||||
self.show_messages()
|
||||
elif choice == 'SEND':
|
||||
self.send_message()
|
||||
print("=" * 80)
|
||||
|
||||
def get_or_create_user(self, username):
|
||||
"""Gets user info from the server, or creates the user.
|
||||
If the user already exists, checks that our public key matches
|
||||
the server's public key for the user.
|
||||
"""
|
||||
try:
|
||||
user = self.api.get_user(username)
|
||||
if user['public_key'] != str(self.public_key):
|
||||
raise ValueError(f"Invalid key for {username}")
|
||||
except APIError:
|
||||
user = self.api.create_user(username, str(self.public_key))
|
||||
return user
|
||||
|
||||
def choose_action_from_menu(self):
|
||||
"""Gets a choice from the user.
|
||||
"""
|
||||
print("What would you like to do?")
|
||||
print("1. See messages")
|
||||
print("2. Send a message")
|
||||
while True:
|
||||
choice = input("> ").strip()
|
||||
if choice == '1':
|
||||
return 'VIEW'
|
||||
elif choice == '2':
|
||||
return 'SEND'
|
||||
|
||||
def show_messages(self):
|
||||
messages = self.api.get_messages(self.username)['messages']
|
||||
if messages:
|
||||
for i, message in enumerate(messages):
|
||||
cleartext = self.private_key.decrypt(message['ciphertext'])
|
||||
print('-' * 80)
|
||||
print(f"{i}. From {message['sender']}:")
|
||||
print(cleartext)
|
||||
else:
|
||||
print(f"No messages for {self.username}.")
|
||||
|
||||
def send_message(self):
|
||||
try:
|
||||
recipient_name = input("Username of recipient: ").strip()
|
||||
recipient = self.api.get_user(recipient_name)
|
||||
except APIError:
|
||||
print(f"No user named {recipient_name}.")
|
||||
return
|
||||
recipient_public_key = PublicKey.load(recipient['public_key'])
|
||||
plaintext = input("Message: ")
|
||||
ciphertext = recipient_public_key.encrypt(plaintext)
|
||||
time_sent = self.get_current_time()
|
||||
time_sent_signature = self.private_key.sign(time_sent)
|
||||
self.api.send_message(
|
||||
self.username,
|
||||
recipient_name,
|
||||
ciphertext,
|
||||
time_sent,
|
||||
time_sent_signature,
|
||||
)
|
||||
print('-' * 80)
|
||||
print(f"Message sent to {recipient_name}")
|
||||
|
||||
|
||||
def get_current_time(self):
|
||||
return datetime.utcnow().isoformat()
|
||||
|
||||
def main():
|
||||
from argparse import ArgumentParser
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("username")
|
||||
parser.add_argument("-k", "--key", default="subrosa_private_key.pem")
|
||||
parser.add_argument("-s", "--server-url", default="http://127.0.0.1:5000")
|
||||
args = parser.parse_args()
|
||||
ui = SubRosaUI(args.key, args.server_url)
|
||||
ui.run(args.username)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Reference in New Issue
Block a user