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
|
|
|
|
|
|
|
|
|
|
|
#### logging ####
|
|
|
|
# json decoder for int keys
|
2018-11-15 20:29:32 +01:00
|
|
|
from Crypto.Cipher import PKCS1_OAEP
|
|
|
|
from Crypto.PublicKey import RSA
|
|
|
|
from Crypto.Random import get_random_bytes
|
|
|
|
|
|
|
|
|
|
|
|
def make_hashable(o):
|
|
|
|
if isinstance(o, (tuple, list)):
|
|
|
|
return tuple((make_hashable(e) for e in o))
|
|
|
|
elif isinstance(o, dict):
|
|
|
|
return tuple(sorted((k, make_hashable(v)) for k, v in o.items()))
|
|
|
|
elif isinstance(o, (set, frozenset)):
|
|
|
|
return tuple(sorted(make_hashable(e) for e in o))
|
|
|
|
return o
|
2018-11-12 20:13:56 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Decoder(json.JSONDecoder):
|
|
|
|
def decode(self, s, **kwargs):
|
|
|
|
result = super().decode(s) # result = super(Decoder, self).decode(s) for Python 2.x
|
|
|
|
return self._decode(result)
|
|
|
|
|
|
|
|
def _decode(self, o):
|
|
|
|
if isinstance(o, str):
|
|
|
|
try:
|
|
|
|
return int(o)
|
|
|
|
except ValueError:
|
|
|
|
return o
|
|
|
|
elif isinstance(o, dict):
|
|
|
|
return {k: self._decode(v) for k, v in o.items()}
|
|
|
|
elif isinstance(o, list):
|
|
|
|
return [self._decode(v) for v in o]
|
|
|
|
else:
|
|
|
|
return o
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
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 ####
|
|
|
|
class clientThread(threading.Thread):
|
|
|
|
"""Main thread, for each client"""
|
|
|
|
|
|
|
|
def __init__(self, clientsocket, ip, port):
|
|
|
|
"""Create clientThread object
|
|
|
|
|
|
|
|
:param clentsocket: Client's socket
|
|
|
|
:param ip: 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
|
|
|
|
:type ip: str
|
|
|
|
:type port: int
|
|
|
|
|
|
|
|
:return: Nothing
|
|
|
|
:rtype: NoneType"""
|
|
|
|
debug("Creation du thread pour %s" % ip)
|
|
|
|
threading.Thread.__init__(self) # initialisation du thread
|
|
|
|
self.client = clientsocket
|
|
|
|
self.ip = ip
|
|
|
|
self.port = port
|
2018-11-15 20:29:32 +01:00
|
|
|
self.running = True
|
|
|
|
self.status = None
|
2018-11-12 20:13:56 +01:00
|
|
|
debug("Creation du thread pour %s reussie" % ip)
|
|
|
|
|
2018-11-15 20:29:32 +01:00
|
|
|
def initialize(self):
|
|
|
|
"""Interpret request header"""
|
|
|
|
# Receive message
|
|
|
|
chunk = bytes("", "ascii")
|
|
|
|
while chunk != BEGIN_MESSAGE:
|
|
|
|
chunk = self.client.recv(BUFFER_SIZE)
|
|
|
|
last_chunk = chunk
|
|
|
|
while last_chunk != END_MESSAGE:
|
|
|
|
last_chunk = self.client.recv(BUFFER_SIZE)
|
|
|
|
chunk += last_chunk
|
|
|
|
# Remove the begening and the end of chunk
|
|
|
|
util_part = chunk[BUFFER_SIZE:-BUFFER_SIZE]
|
|
|
|
print(util_part)
|
|
|
|
header = util_part[:BUFFER_SIZE]
|
|
|
|
print(header)
|
|
|
|
content = util_part[BUFFER_SIZE:]
|
|
|
|
header_txt = header.decode("ascii").rstrip(";")
|
|
|
|
version = header_txt.split("\n")[0]
|
|
|
|
# Extraction des champs du header
|
|
|
|
if version != VERSION:
|
|
|
|
debug("Pas la bonne version")
|
|
|
|
self.send(b"Error".ljust(BUFFER_SIZE, b";"))
|
|
|
|
champs = {l.split(": ")[0]: l.split(": ")[1] for l in header_txt.split('\n')[1:]}
|
|
|
|
|
|
|
|
if champs.get("type", None) is None:
|
|
|
|
debug("Le champ type n'est pas dans le header")
|
|
|
|
self.send(b"Error".ljust(BUFFER_SIZE, b";"))
|
|
|
|
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' ')
|
|
|
|
self.aes_key = get_random_bytes(64)
|
2018-11-16 19:59:22 +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";")
|
|
|
|
self.send_rsa(header+content)
|
|
|
|
return
|
|
|
|
|
|
|
|
def send_rsa(self, to_send):
|
|
|
|
if self.rsa_client is None:
|
|
|
|
debug("Clef RSA non recue")
|
|
|
|
self.client.send(b"Error".ljust(BUFFER_SIZE))
|
|
|
|
return
|
|
|
|
# Transform rsakey and generate sessionkey
|
|
|
|
recipient_key = RSA.import_key(self.rsa_client)
|
|
|
|
# RSA crypter object
|
|
|
|
cipher_rsa = PKCS1_OAEP.new(recipient_key)
|
|
|
|
# Encrypt session_key
|
2018-11-16 19:59:22 +01:00
|
|
|
crypted = b""
|
|
|
|
for to_send_text in [to_send[i:i + 16] for i in range(0, len(to_send), 16)]:
|
|
|
|
crypted += cipher_rsa.encrypt(to_send_text)
|
|
|
|
self.send(crypted)
|
2018-11-15 20:29:32 +01:00
|
|
|
|
|
|
|
def send(self, to_send):
|
|
|
|
self.client.send(BEGIN_MESSAGE)
|
2018-11-16 19:59:22 +01:00
|
|
|
i=0
|
2018-11-15 20:29:32 +01:00
|
|
|
for to_send_text in [to_send[i:i + BUFFER_SIZE] for i in range(0, len(to_send), BUFFER_SIZE)]:
|
|
|
|
self.client.send(to_send_text.ljust(BUFFER_SIZE, b";"))
|
2018-11-16 19:59:22 +01:00
|
|
|
i+=1
|
|
|
|
print(i)
|
2018-11-15 20:29:32 +01:00
|
|
|
self.client.send(END_MESSAGE)
|
|
|
|
|
|
|
|
|
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-15 20:29:32 +01:00
|
|
|
self.initialize()
|
|
|
|
print("done")
|
2018-11-12 20:13:56 +01:00
|
|
|
self.client.close()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
clients = []
|
|
|
|
while True:
|
|
|
|
main_socket.listen(1) # ecoutes des connections entrantes
|
2018-11-12 20:42:44 +01:00
|
|
|
clientsocket, (ip, PORT) = main_socket.accept()
|
|
|
|
newClient = clientThread(clientsocket, ip, PORT)
|
2018-11-12 20:13:56 +01:00
|
|
|
newClient.start()
|
|
|
|
clients.append(newClient)
|