secure-chat/server/main.py

232 lines
7.5 KiB
Python
Raw Normal View History

2018-11-12 21:46:27 +01:00
#!/usr/bin/env python3
2018-11-12 20:13:56 +01:00
# coding: utf8
import json
import logging
2018-11-13 21:37:49 +01:00
import logging.config
2018-11-12 20:13:56 +01:00
import os
import socket
2018-11-15 20:29:32 +01:00
import threading
2018-11-12 20:13:56 +01:00
2018-11-18 12:52:55 +01:00
try:
# noinspection PyUnresolvedReferences
from Crypto.PublicKey import RSA as RSA
# noinspection PyUnresolvedReferences
from Crypto.Cipher import PKCS1_OAEP as PKCS1_OAEP
# noinspection PyUnresolvedReferences,PyProtectedMember
from Crypto.Random._UserFriendlyRNG import get_random_bytes as get_random_bytes
pycryptodome = False
except ModuleNotFoundError: # Pycryptodomex
from Cryptodome.PublicKey import RSA as RSA
from Cryptodome.Cipher import PKCS1_OAEP as PKCS1_OAEP
from Cryptodome.Random import get_random_bytes as get_random_bytes
pycryptodome = True
2018-11-12 20:13:56 +01:00
def setup_logging(default_path='log_config.json', default_level=logging.INFO, env_key='LOG_CFG'):
"""Setup logging configuration
"""
path = default_path
value = os.getenv(env_key, None)
if value:
path = value
if os.path.exists(path):
with open(path, 'rt') as f:
config = json.load(f)
logging.config.dictConfig(config)
else:
logging.basicConfig(level=default_level)
setup_logging()
log_server = logging.getLogger('server')
debug = log_server.debug
info = log_server.info
warning = log_server.warning
error = log_server.error
critical = log_server.critical
#### Variables ####
2018-11-12 20:42:44 +01:00
HOST = ''
PORT = 8888
2018-11-15 20:29:32 +01:00
BUFFER_SIZE = 4096
2018-11-18 12:52:55 +01:00
CHUNK_SIZE = int(BUFFER_SIZE / 8)
2018-11-15 20:29:32 +01:00
BEGIN_MESSAGE = bytes("debut".ljust(BUFFER_SIZE, ";"), "ascii")
END_MESSAGE = bytes("fin".ljust(BUFFER_SIZE, ";"), "ascii")
VERSION = "EICP2P2 V1"
DONE = 0
ERROR = 1
2018-11-12 20:13:56 +01:00
#### Socket ####
main_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2018-11-12 20:42:44 +01:00
main_socket.bind((HOST, PORT))
2018-11-12 20:13:56 +01:00
2018-11-15 20:29:32 +01:00
2018-11-12 20:13:56 +01:00
#### Threads ####
2018-11-18 12:52:55 +01:00
class ClientThread(threading.Thread):
2018-11-12 20:13:56 +01:00
"""Main thread, for each client"""
2018-11-18 12:52:55 +01:00
def __init__(self, clientsocket, ip_client, port):
"""Create ClientThread object
2018-11-12 20:13:56 +01:00
2018-11-18 12:52:55 +01:00
:param clientsocket: Client's socket
:param ip_client: Client's ip address
2018-11-12 20:42:44 +01:00
:param port: Client's connection PORT
2018-11-12 20:13:56 +01:00
:type clientsocket: socket.socket
2018-11-18 12:52:55 +01:00
:type ip_client: str
2018-11-12 20:13:56 +01:00
:type port: int
:return: Nothing
:rtype: NoneType"""
2018-11-18 12:52:55 +01:00
debug("Creation du thread pour %s" % ip_client)
2018-11-12 20:13:56 +01:00
threading.Thread.__init__(self) # initialisation du thread
self.client = clientsocket
2018-11-18 12:52:55 +01:00
self.ip = ip_client
2018-11-12 20:13:56 +01:00
self.port = port
2018-11-15 20:29:32 +01:00
self.running = True
self.status = None
2018-11-18 12:52:55 +01:00
self.rsa_client = None
self.aes_key = get_random_bytes(64)
debug("Creation du thread pour %s reussie" % ip_client)
2018-11-12 20:13:56 +01:00
2018-11-15 20:29:32 +01:00
def initialize(self):
2018-11-18 12:52:55 +01:00
"""Initialize connection with client
:rtype: NoneType
:return: Nothing"""
2018-11-15 20:29:32 +01:00
# Receive message
2018-11-18 12:52:55 +01:00
message = self.receive()
# Get informations
header = message[:BUFFER_SIZE]
content = message[BUFFER_SIZE:]
# Extract header data
2018-11-15 20:29:32 +01:00
header_txt = header.decode("ascii").rstrip(";")
version = header_txt.split("\n")[0]
# Extraction des champs du header
if version != VERSION:
2018-11-18 12:52:55 +01:00
debug("Incorrect protocol version.")
self.send(b"Error")
# Extract information from header
2018-11-15 20:29:32 +01:00
champs = {l.split(": ")[0]: l.split(": ")[1] for l in header_txt.split('\n')[1:]}
if champs.get("type", None) is None:
2018-11-18 12:52:55 +01:00
debug("The type field is not in the header")
self.send(b"Error")
2018-11-15 20:29:32 +01:00
return
if self.status is None and champs["type"] != "RSASend":
debug("Requête différente de RASSend avec une connection non initialisée")
self.send(b"Error".ljust(BUFFER_SIZE, b";"))
return
if champs["type"] == "RSASend":
debug("Réception de la clef RSA de %s", self.ip)
self.rsa_client = content.rstrip(b";").rstrip(b' ')
2018-11-16 21:54:30 +01:00
header = bytes((VERSION + "\ntype: init").ljust(BUFFER_SIZE, ";"), 'ascii')
2018-11-15 20:29:32 +01:00
content = self.aes_key.ljust(BUFFER_SIZE, b";")
2018-11-16 21:54:30 +01:00
self.send_rsa(header + content)
2018-11-15 20:29:32 +01:00
return
2018-11-18 12:52:55 +01:00
################################################ COMMUNICATION WITH RSA ################################################
def send_rsa(self, to_send, key=None):
"""Send message with rsa encryption
:param to_send: Message to send
:type to_send: bytes
:param key: key to replace self.client_key
:type key: bytes
:rtype: NoneType
:return: Nothing"""
debug(b"Send with RSA encryption: " + to_send + bytes(str(self.ip), "ascii"))
if key is None:
key = self.rsa_client
if key is None:
info("RSA key not received, connection failure.")
self.client.send(b"Error".ljust(BUFFER_SIZE, bytes(1)))
2018-11-15 20:29:32 +01:00
return
2018-11-18 12:52:55 +01:00
# Get RSA key
print(len(to_send))
recipient_key = RSA.importKey(key)
# RSA encryption object
2018-11-15 20:29:32 +01:00
cipher_rsa = PKCS1_OAEP.new(recipient_key)
2018-11-18 12:52:55 +01:00
encrypted = b""
for to_send_text in [to_send[i:i + CHUNK_SIZE] for i in range(0, len(to_send), CHUNK_SIZE)]:
print(len(to_send_text))
encrypted += cipher_rsa.encrypt(to_send_text)
print(len(encrypted))
self.send(encrypted)
return None
############################################ COMMUNICATION WITHOUT CRYPTING ############################################
def receive(self):
"""Receive message from connection
:rtype: bytes
:return: Message's content"""
chunk = bytes("", "ascii") # Temp variable to store received datas
while chunk != BEGIN_MESSAGE:
chunk = self.client.recv(BUFFER_SIZE)
content = b''
while chunk != END_MESSAGE:
chunk = self.client.recv(BUFFER_SIZE)
# Get only interesting chucks
if chunk != END_MESSAGE:
# Get content part
# int.from_bytes(chunk[:2], byteorder='big') == Get content size
content += chunk[2:int.from_bytes(chunk[:2], byteorder='big')]
debug(b"Received from" + bytes(str(self.ip), ascii) + b" : " + content)
return content
2018-11-15 20:29:32 +01:00
def send(self, to_send):
2018-11-18 12:52:55 +01:00
"""Send message to connection
:param to_send: message to send
:type to_send: bytes
:return: Nothing
:rtype: NoneType"""
debug(b"Send " + to_send + b" to " + bytes(str(self.ip), "ascii"))
# Sending the message start
2018-11-15 20:29:32 +01:00
self.client.send(BEGIN_MESSAGE)
2018-11-16 21:54:30 +01:00
i = 0
2018-11-18 12:52:55 +01:00
print(len(to_send))
2018-11-16 21:54:30 +01:00
for to_send_text in [to_send[i:i + BUFFER_SIZE - 2] for i in range(0, len(to_send), BUFFER_SIZE - 2)]:
2018-11-18 12:52:55 +01:00
self.client.send(
(len(to_send_text)).to_bytes(2, byteorder='big') # Size of the message contained by the chunk
+ to_send_text.ljust(BUFFER_SIZE - 2, bytes(1)) # Content of the chunk
)
2018-11-16 21:54:30 +01:00
i += 1
2018-11-18 12:52:55 +01:00
# Sending the message stop
2018-11-15 20:29:32 +01:00
self.client.send(END_MESSAGE)
2018-11-18 12:52:55 +01:00
return None
2018-11-15 20:29:32 +01:00
2018-11-12 20:13:56 +01:00
def run(self): # main de la connection du client
"""Run thread mainloop
:return: Nothing
:rtype: NoneType"""
2018-11-18 12:52:55 +01:00
info(self.ip + "connected, initialize connection...")
2018-11-15 20:29:32 +01:00
self.initialize()
2018-11-18 12:52:55 +01:00
info(self.ip + "connection initialized.")
2018-11-15 20:29:32 +01:00
print("done")
2018-11-12 20:13:56 +01:00
self.client.close()
if __name__ == "__main__":
clients = []
while True:
2018-11-18 12:52:55 +01:00
main_socket.listen(1) # Waiting for incoming connections
client_socket, (ip, PORT) = main_socket.accept()
newClient = ClientThread(client_socket, ip, PORT)
2018-11-12 20:13:56 +01:00
newClient.start()
clients.append(newClient)