From d4a9f911b5fde2bc7d9c889eda3ee6ab75323f4b Mon Sep 17 00:00:00 2001 From: = Date: Tue, 12 Nov 2019 20:32:33 +0100 Subject: [PATCH] SessionID implemented --- .client_test_3.py.swp | Bin 0 -> 12288 bytes .clients_keys | 14 +++ .server_test_3.py.swp | Bin 0 -> 20480 bytes client_test_3.py | 52 +++++++-- server_test_3.py | 22 ++-- utils/__pycache__/__init__.cpython-36.pyc | Bin 160 -> 158 bytes .../client_keys_manager.cpython-36.pyc | Bin 1600 -> 1590 bytes .../__pycache__/keys_manager_1.cpython-36.pyc | Bin 2938 -> 2936 bytes .../rsa_tenamortech_utils.cpython-36.pyc | Bin 2526 -> 2524 bytes .../server_keys_manager.cpython-36.pyc | Bin 0 -> 1590 bytes .../symmetric_keys_manager_1.cpython-36.pyc | Bin 1960 -> 3145 bytes utils/client_keys_manager.py | 2 +- utils/server_keys_manager.py | 4 +- utils/symmetric_keys_manager_1.py | 103 ++++++++++++++---- 14 files changed, 155 insertions(+), 42 deletions(-) create mode 100644 .client_test_3.py.swp create mode 100644 .server_test_3.py.swp create mode 100644 utils/__pycache__/server_keys_manager.cpython-36.pyc diff --git a/.client_test_3.py.swp b/.client_test_3.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..b8a671a4403c84e5d0ec67f7f323cf77d3780e07 GIT binary patch literal 12288 zcmeHNOK%%h6u#kE9;K*N*?`cAEy%dI&Z8BoOtVN*rwySG)d`h|qS1JKZBOHQXy)39 zD+Fv0Y3t_fOmm);8oxV@WWG#eGl9MJ^-!*XMtCM=YgZZUxyj{ z0{9d#fo0%D;055fCmFj5de}b#8fnA^vDDLjV*#D;{i{vuB6?KMVsbY5BfNLdUjG{{nUk2Q1 zYTQvLKXf~2YZq(v`qKGYnY>=3Ynl2EALs%1``j;;)aQwoNRk%Ga=f0<3fEkJ$8y>< zT_{zc(rt2WM*RVN29rlqljmCwWGS?6fqZUqtIwO%G=xztl+`_w=iPu;Y547kk|tMX z^QJDptzej*GBsyb5C%sNl*95{c>5Slic@H&<6{SM~bZ8?~XV z^e^&l>s)Pl^ZYi|jlQI#VFZ>*7YwIa?-)BAY_;5gw&?OYZBrquAu#$}4+gfPy)n=` zZXh)IPnjIPi$DYc4({kyGa3(Un+rb-(|#cva(D0cRVtNn0~2oc(-&H|c22&l(pG_9 z8nGG=3fqNLWcJe%`$X$}5NQd7uPOiIcPdVkXuD=uI7`*0mg`Gfs2*#O+6ghCOY&)A zc@mZ^b4(f>smsad2=S4 ziDUNt3)LellEnnd`vm$AUZq(&Cs?Z44@TDNz2`~1JPuS_M|qX$XZCy_2kN4#M^{vB zRVkPxT!x)g$x#(LRg67uXCpP@$s|+8wrA!n(j|A7b~$xWgh+I_YypIVNN!6F!!}Ts zBD+?%OVToOdwwD(T=zgWuBuuu`KYUoGS0(DIc&Q^hY@N$g|j20H~bz?H3gYWDS8sI zCJHI{Eu(AQ;IR@-rpdwO=U^KZaU(_+Wt$4g2<=duNy#i3MXNlyr2aThbeg?l0&E22OrsLfFL3j;IJ+x%lD*zkeX=Hijfc%&kq;>+1zMtB*u=c8JN4Qw)AON bVV$c;h^P;940%IW)FFG?Sh*5+|0VV}KwPDd literal 0 HcmV?d00001 diff --git a/.clients_keys b/.clients_keys index ac4d505..e2c1763 100644 --- a/.clients_keys +++ b/.clients_keys @@ -12,3 +12,17 @@ fd05a547-8032-450c-a0f1-6a204aa1288e -n2gtRIBrlDpRisHeno-1pxYLXn5muuct7CHuJXCsbI 20a03a39-d9f0-495b-ae38-016e3350ede3 78J-gIZ710ozVxg4Iu4S9r3olhZCMtcKYRC3_H4hCxU= 0 9c5b6d7c-6317-4db1-a14a-d4be911ef45f u4SdDIORALLuPoKtJ076rZzpJk1ZiUCRWERZwQdNsxY= 0 f6325f18-2909-4665-b681-a0f764fa6c68 _d0ywQ34kEgiRopLnr8R8RDjjDLbH7JTZJp1_FkwgWw= 0 +5dfb41b5-edfa-487b-a000-11f4fc7eb191 DBVK8tjl3vMe08QrSYEWTJb3UOhtCH2A2Gj7xpiqHVs= 0 +2822aedb-22c6-4063-880e-5daddc6f4789 fyH3UTGRpaztPFiLuBezND3-1OrwpgTsj7uemxzTKrs= 0 +05d79777-9f0f-46b8-b69a-67024a37d9e4 XyNF4tFsFb6WgPBybLBLvensO0fglO371E-AGlaGqCI= 0 +f0bf481d-71c6-4f2d-8881-13afdd8901f0 UkS-005VTwKUMnm2mDAHd-y_wsnfqvL8e4N7OxyGcW8= 0 +c3c91ea4-bc4a-424a-a8fc-3daeb0c94002 NTEQ0pW5mImMGwsvnDTPIsmgZv5h95iKrfIjyCdsRI0= 0 +82f0b861-54e5-4cad-a55e-71c0857ac410 nmxMpRZYhhQX5JmsD4AK2Y_CZlFN4Bo6HlO0sbLKwc0= 0 +89982b07-b9c3-4d4a-b31e-88672b06d526 E6jPdbcKhygBLsKjiXg0onftF-6XNv5q6sA_dSoLzJw= 0 +6c584e84-0a4a-4e1f-9e38-c58f740b3303 VCLE6cBO3qT-lQ2zrYoXYI8vnJuHLrvb_CCEC_P56ow= 0 +24bd5c2a-8f30-4a8f-999a-22532a2bb42c v4u0W88oesXaSCBE81dgMwLQDdf1zsvmiS63xG4vo-0= 0 +ca902056-92e8-4a63-8498-382003098cdf tO3N2oc7C_Pwhdcp6g3XcC-IR4yAw8CHQ5FOmkce2AA= 0 +3543e331-a8d3-42a0-9e25-c1b4349c8682 JuVYT6cTJ-vJgcfeBeVU4dxt3x5sLEqG2kgdc1E5nkA= 0 +8f5807f5-28c4-494c-b961-6c59c556733d T1kflpNULJtgoyjcgF10OM3jDWZ3gN3hWhS3S1x9GbU= 0 +ee0b2669-63e8-4aa3-ac22-039f0c70188e XEzFplSYOZFjVUBU1YTWuz64dsBwxH3PgblNr7wxzAc= 0 +bb0e3173-4df3-4b39-95b8-9214bda026f7 lxhvklnDrhayi14jSq6_4wMbODuaNYezwCW7rI7N-s4= 0 diff --git a/.server_test_3.py.swp b/.server_test_3.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..40c56037c8b955a8d0fe4992aad8acf46f5fc4d7 GIT binary patch literal 20480 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 literal 0 HcmV?d00001 diff --git a/client_test_3.py b/client_test_3.py index 6abcd39..4b4a812 100644 --- a/client_test_3.py +++ b/client_test_3.py @@ -11,6 +11,7 @@ from cryptography.hazmat.primitives import serialization # Home made RSA Utils from utils.rsa_tenamortech_utils import * from utils.client_keys_manager import * +from utils.symmetric_keys_manager_1 import * host = socket.gethostname() port = 2004 @@ -30,19 +31,46 @@ def de_serialize_pub_key(public_key_pem): while MESSAGE != 'exit': data = tcpClientA.recv(BUFFER_SIZE) if not got_pub_key_server: - public_key_server = de_serialize_pub_key(data) - got_pub_key_server = True - # Now we have the pub key of the server, we will send our pub key too - #encrypted_public_key = encrypt_msg(public_key_pem, public_key_server) - #encrypted_public_key = encrypt_msg(bytes("test123456789000000000000000000000000000iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii000", 'utf-8'), public_key_server) - print("Server public key received") - print("Sending current client public key [ ... ] ") - tcpClientA.send(public_key_pem) - print("Sending current client public key [ OK ] ") - + print("[DEBUG] Checking for SessionID [ ... ]") + (session_id,symmetric_key,public_key_server) = reload_session_by_host(host) + print("[DEBUG] Checking for SessionID [ OK ]") + if(session_id != -1): + tcpClientA.send(encrypt_msg(session_id.encode('utf-8'), public_key_server)) + got_pub_key_server = True + print("[DEBUG] SessionID Found ! [ OK ]") + else: + #if find sessionID load it and negociate with server + #else send public key ... + print("[DEBUG] Receiving Server Public Key [ ... ]") + public_key_server = de_serialize_pub_key(data) + got_pub_key_server = True + print("[DEBUG] Receiving Server Public Key [ OK ]") + # Now we have the pub key of the server, we will send our pub key too + print("[DEBUG] Sending current Client Public Key [ ... ] ") + tcpClientA.send(public_key_pem) + print("[DEBUG] Sending current Client Public Key [ OK ] ") + print("[DEBUG] Waiting for new Symmetric Key and New SessionID [ ... ] ") + data = tcpClientA.recv(BUFFER_SIZE) + new_symmetric_key_and_session_id = decrypt_msg(data, private_key) + new_symmetric_key_and_session_id = new_symmetric_key_and_session_id.decode('utf-8') + new_symmetric_key_and_session_id = new_symmetric_key_and_session_id.split('|') + new_symmetric_key = new_symmetric_key_and_session_id[0] + new_session_id = new_symmetric_key_and_session_id[1] + + print("[DEBUG] Waiting for new Symmetric Key and New SessionID [ OK ] ") + print("[DEBUG] Symmetric Key: [" + str(new_symmetric_key) + "]") + print("[DEBUG] SessionID : [" + new_session_id + "]") + + print("[DEBUG] Saving basic HandShake infos [ ... ] ") + save_sym_key_by_host(host, new_session_id, new_symmetric_key, public_key_server) + print("[DEBUG] Saving basic HandShake infos [ OK ] ") + data = tcpClientA.recv(BUFFER_SIZE) + decrypted_data = decrypt_msg(data, private_key) + print(decrypted_data.decode('utf-8')) + else: print(" Client2 received data:", data) - MESSAGE = input("tcpClientA: Enter message to continue/ Enter exit:").encode('utf-8') - tcpClientA.send(MESSAGE) + MESSAGE = input("Working !").encode('utf-8') + tcpClientA.send(encrypt_msg(MESSAGE, public_key_server)) tcpClientA.close() diff --git a/server_test_3.py b/server_test_3.py index 8d8884a..533caa3 100644 --- a/server_test_3.py +++ b/server_test_3.py @@ -1,7 +1,8 @@ import socket from threading import Thread from socketserver import ThreadingMixIn -from utils.keys_manager_1 import * +#from utils.keys_manager_1 import * +from utils.server_keys_manager import * from utils.symmetric_keys_manager_1 import * from utils.rsa_tenamortech_utils import * # --- Init keys --- @@ -39,7 +40,8 @@ class ClientThread(Thread): print("[DEBUG] Waiting for SessionID validation [ ... ]") msg = decrypt_msg(data, private_key) if(len(msg) == 36): # SessionID length should be 36 char. - print("[DEBUG] SessionID format looks correct [ ~ ]") + print("[DEBUG] SessionID format looks correct [ ~ ]") + print("[DEBUG] (Given SessionID: [" + msg.decode('utf-8') + "]") session_id = msg (symmetric_key,public_key_client) = reload_session_sym_key(session_id) @@ -48,27 +50,33 @@ class ClientThread(Thread): if(symmetric_key == -1): print("[DEBUG] Waiting for SessionID validation [ FAIL ]") print("[DEBUG] Invalid SessionID, will now abort negociation [ FAIL ]") + conn.close() break else: able_to_retrieve_session_id = True print("[DEBUG] Waiting for SessionID validation [ OK ]") print("[DEBUG] SessionID is valid, symmetric_key and current Client Public key were retrieved successfully") print("[DEBUG] I'm all yours... [ :) ]") + conn.send(encrypt_msg("login:".encode('utf-8'), public_key_client)) # --- SessionID that the client sent is valid, we got all the required infos # --- Client sent did not send sessionID but sent his public Key # --- Create a new session for it, (gen sessionID, create new symmetric Key, store his public key) if able_to_retrieve_session_id == False: print("[DEBUG] No existing Session, will now initiate new Session [ ... ]") # --- This function will create a sessionID, return a symmetric key and store the pub key - symmetric_key = gen_sym_key_and_save(public_key_client) - print("[DEBUG] No existing Session, will now initiate new Session [ OK ]") - print("[DEBUG] I'm all yours... [ :) ]") + (symmetric_key,session_id) = gen_sym_key_and_save(public_key_client) + print("[DEBUG] Sending encrypted Symmetric Key to Client [ ... ]") + sym_key_and_session_id = (str((symmetric_key).decode('utf-8')) + "|" + session_id).encode('utf-8') + conn.send(encrypt_msg(sym_key_and_session_id, public_key_client)) + print("[DEBUG] Sending encrypted Symmetric Key to Client [ OK ]") + print("[DEBUG] I'm all yours... [ :) ]") + conn.send(encrypt_msg("login:".encode('utf-8'), public_key_client)) - print("Server received data:", data) + #print("Server received data:", data) MESSAGE = input("Multithreaded Python server : Enter Response from Server/Enter exit:").encode('utf-8') if MESSAGE == 'exit': break - conn.send(MESSAGE) # echo + conn.send(MESSAGE) # echo # Multithreaded Python server : TCP Server Socket Program Stub TCP_IP = '0.0.0.0' diff --git a/utils/__pycache__/__init__.cpython-36.pyc b/utils/__pycache__/__init__.cpython-36.pyc index e6e4fcc3f6a3fd4367c4ff2cfa664bbd9b867459..1233d6069e15fa1fff31450442a9e8c6a3817588 100644 GIT binary patch delta 23 fcmZ3$IFFItn3tE!?#!u)>=umP6CKPaR;mL4ObG_a delta 25 hcmbQoxPXz}n3tDpPSxIt>=ul^6CKPM4JVdq003BJ2KfL0 diff --git a/utils/__pycache__/client_keys_manager.cpython-36.pyc b/utils/__pycache__/client_keys_manager.cpython-36.pyc index c58fb0cabfd93ada53e3cfc8fafde8909608f621..43ca59f9eb4c13f5b53bbb9d5d7640f372b94f04 100644 GIT binary patch delta 73 zcmX@WvyDgDn3tDJ@5ZUvOFRq=j~S2v6Oioy#KkTXm7O={*sw5$Ohq!4i#!Yrj~S2v6Oioy#Km3{m7UpAIhz?688)WburNkUUd&?7 ZXt?<%i$9|PP!|IaBM&nOaxih|0s!5T4(k8_ diff --git a/utils/__pycache__/keys_manager_1.cpython-36.pyc b/utils/__pycache__/keys_manager_1.cpython-36.pyc index 88cf82500487908faad1ada9c4537d87ef153468..cec09c68a0fa1412932be125abc135f8e05c9a52 100644 GIT binary patch delta 157 zcmew*_Ct)_n3tE!?#!u;>{X16fs>mU%{T96jA3HDHCckiol$gi0*fdkkSb&4W@J>} z+{9+Z$SA&fFZ&5b2}uS9hFff@6`92)#YNIUPLV8#kefW0Q&ahta87<=N_;_TZhS#$ uQch-ae0FMO5g$l_GLX<@D^i^Nl5-iO#^ecHa|A>|;v6u^B8|y5+;#w>0V%cs delta 170 zcmew%_DhW2n3tDJ^U0x&>{X16!IPU9%^3|h?_i8&;)voaC{4=AOrGq>EHT-XMT1df za~z8(BakX#)E;n^RLc zN;oG!F(tkrH8&n&WPEmNWf31pffA6=WGhmb{G4+cquS&?t~mlCAaM?uWRcorD{eag Do?R?N diff --git a/utils/__pycache__/rsa_tenamortech_utils.cpython-36.pyc b/utils/__pycache__/rsa_tenamortech_utils.cpython-36.pyc index 44e70538e132636685d7891e66904b61e0ded5d6..a87b88e3cd2ba59b62116337e891e85a18689931 100644 GIT binary patch delta 26 icmca7d`Fngn3tE!?#xEE9gK_-llL>4Z~npfj2!@UQ3*5v delta 28 kcmca3d{3Con3tEUY2!w=9gK`ollL>4Ga7FG%J`fe0EOfUj{pDw diff --git a/utils/__pycache__/server_keys_manager.cpython-36.pyc b/utils/__pycache__/server_keys_manager.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c4f741079c97968d8b860ef761e23df35b311e8 GIT binary patch literal 1590 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 b8C=Q*Z5E4~KRd3!(Kk4j