├── LICENSE.md ├── README.md ├── messenger_client ├── encryption.py ├── messenger_client.py └── new_chat.py └── messenger_server ├── encryption.py └── messenger_server.py /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-messenger 2 | 3 | A secure messaging application built with python 4 | ``` 5 | Version : 1.0 6 | Author : Teja Swaroop 7 | ``` 8 | 9 | 10 | This messenger is built to enable the clients to exchange text messages. The clients connect to a server (hosted on cloud) and they will be able to securely exchange messages with each other through the server. 11 | 12 | The encryption features for this messenger are : 13 | 1. All the messages exchanged between the client and server are encrypted with RSA algorithm (using pycrypto module) 14 | 2. Digital signatures are included to check the authenticity of any received message (also by using pycrypto module) 15 | 16 | ###### Note : Though this application enables secure transmission of messages, it may still be vulnerable to sql injection attacks, you can make simple modifications in the code to avoid these attacks. Any found vulnerability will be fixed in the next versions. 17 | 18 | 19 | # Installation Guide 20 | I tried to bundle all the required modules into a requirements.txt but I failed because some modules need some special attention to be installed on your computer, and they also depend on the OS you're using. So please don't hate me. 21 | Find your operating system below and follow the instructions mentioned. 22 | 23 | ## Client 24 | ------------------------------------------------------------------------------- 25 | 26 | WINDOWS : 27 | Required modules - pycrypto,keyboard 28 | 29 | Open your cmd as administrator and run each following command one by one 30 | 31 | For Windows 32-bit : 32 | ```easy_install http://www.voidspace.org.uk/python/pycrypto-2.6.1/pycrypto-2.6.1.win32-py2.7.exe``` 33 | 34 | For Windows 64-bit : 35 | ```easy_install http://www.voidspace.org.uk/python/pycrypto-2.6.1/pycrypto-2.6.1.win-amd64-py2.7.exe``` 36 | 37 | ```pip install keyboard``` 38 | 39 | 40 | 41 | ------------------------------------------------------------------------------- 42 | 43 | 44 | 45 | MAC OS : 46 | Required modules - appscript, pynput, pycrypto 47 | 48 | Open your terminal as root, and run each following command one by one 49 | 50 | ```easy_install pycrypto``` 51 | 52 | ```pip install appscript``` 53 | 54 | ```pip install pynput``` 55 | 56 | NOTE : If you cannot install pycrypto with easy_install try installing with pip, if things are still not in track, try doing: 57 | 58 | ```pip uninstall pycrypto``` 59 | 60 | ```easy_install pycrypto``` 61 | 62 | ```pip install pynput``` 63 | 64 | 65 | 66 | ------------------------------------------------------------------------------- 67 | 68 | 69 | 70 | LINUX : 71 | Required modules - pynput,pyrcrypto 72 | 73 | Open your terminal as root, and run each following command one by one 74 | 75 | ```apt-get install autoconf g++ python2.7-dev``` 76 | 77 | ```pip install pycrypto``` 78 | 79 | ```pip install pynput``` 80 | 81 | ## Server 82 | Required modules - pycrypto 83 | Open your terminal as root and execute the following commands 84 | 85 | ```apt-get install autoconf g++ python2.7-dev``` 86 | 87 | ```pip install pycrypto``` 88 | 89 | # Usage 90 | 1. Run the server on your cloud or your local computer(if you are just testing it) 91 | ``` 92 | nohup python messenger_server.py & 93 | ``` 94 | 2. Download the messenger_client folder to your computer and don't change the paths of any of the files. 95 | 96 | Now, run the messenger_client.py by mentioning your server address as a sys argument 97 | ``` 98 | python messenger_server.py 127.0.0.1 99 | ``` 100 | For more info about this application, read my blog post at [Tech Raj Blog](https://blog.techraj156.com/). 101 | -------------------------------------------------------------------------------- /messenger_client/encryption.py: -------------------------------------------------------------------------------- 1 | import Crypto 2 | from Crypto.PublicKey import RSA 3 | from Crypto import Random 4 | from Crypto.Hash import SHA256 5 | from Crypto.Signature import PKCS1_v1_5 6 | from Crypto.PublicKey import RSA 7 | from Crypto.Cipher import PKCS1_OAEP 8 | import ast 9 | import hashlib 10 | import codecs 11 | from base64 import ( 12 | b64encode, 13 | b64decode, 14 | ) 15 | 16 | 17 | 18 | def genkeys(public_fname,private_fname): 19 | random_generator = Random.new().read 20 | key = RSA.generate(1024, random_generator) #generate pub and priv key 21 | public = key.publickey().exportKey('PEM').decode('ascii') 22 | private = key.exportKey('PEM').decode('ascii') 23 | f = open(private_fname+".pem","w") 24 | f.write(private) 25 | f.close() 26 | f = open(public_fname+".pem","w") 27 | f.write(public) 28 | f.close() 29 | return public 30 | 31 | 32 | def encrypt(text,fname,publickey): 33 | if(publickey==None): 34 | publickey = RSA.importKey(open(fname, "rb")) 35 | else: 36 | publickey = RSA.importKey(publickey) 37 | 38 | 39 | hexify = codecs.getencoder("hex") 40 | encrypted = publickey.encrypt(text, 32) 41 | encrypted2 = hexify(encrypted[0])[0] 42 | return encrypted2 43 | 44 | def decrypt(cipher,fname): 45 | privatekey = RSA.importKey(open(fname, "rb")) 46 | decrypted = privatekey.decrypt(str(cipher)) 47 | return decrypted 48 | 49 | 50 | def hash_string(text): 51 | digest = SHA256.new() 52 | digest.update(text) 53 | return digest 54 | 55 | def signature(text,private_fname): 56 | digest = SHA256.new() 57 | digest.update(text) 58 | privatekey = RSA.importKey(open(private_fname+".pem", "rb")) 59 | signer = PKCS1_v1_5.new(privatekey) 60 | sig = signer.sign(digest) 61 | hexify = codecs.getencoder("hex") 62 | sig = hexify(sig)[0] 63 | return sig 64 | 65 | 66 | def check_authenticity(text,signature,publickey): 67 | publickey = RSA.importKey(publickey.encode()) 68 | verifier = PKCS1_v1_5.new(publickey) 69 | digest = SHA256.new() 70 | digest.update(text) 71 | verified = verifier.verify(digest, signature) 72 | 73 | if(verified): 74 | return 1 75 | else: 76 | return 0 77 | 78 | -------------------------------------------------------------------------------- /messenger_client/messenger_client.py: -------------------------------------------------------------------------------- 1 | #messenger_client.py 2 | import socket 3 | import threading 4 | import sys 5 | import ast 6 | import getpass 7 | import time 8 | import signal 9 | import os 10 | import platform 11 | import subprocess 12 | import random 13 | import encryption 14 | import codecs 15 | 16 | if(not(os.name=="nt")): 17 | import appscript 18 | 19 | 20 | class bcolors: 21 | HEADER = '\033[95m' 22 | OKBLUE = '\033[94m' 23 | OKGREEN = '\033[92m' 24 | WARNING = '\033[93m' 25 | FAIL = '\033[91m' 26 | ENDC = '\033[0m' 27 | BOLD = '\033[1m' 28 | UNDERLINE = '\033[4m' 29 | 30 | 31 | 32 | sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 33 | username = None 34 | server_sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 35 | commands = {":login" : "Login to an existing account", ":register" : "Create an account",":chat" : "Chat with an online user",":showonline" : "Show online users", ":logout" : "Logout from the account",":quitchat":"Quit chat"} 36 | chats = dict() 37 | 38 | def print_to_screen(op,color,msg): 39 | lock = threading.Lock() 40 | try: 41 | if(op==1): 42 | lock.acquire() 43 | print(color + msg + bcolors.ENDC) 44 | lock.release() 45 | return 46 | elif(op==2): 47 | inp = raw_input(color+msg+bcolors.ENDC) 48 | return inp 49 | except: 50 | #lock.release() 51 | return 52 | 53 | 54 | def clear_screen(): 55 | os.system('cls' if os.name == 'nt' else 'clear') 56 | 57 | 58 | def show_commands_menu(): 59 | print(bcolors.OKBLUE+"\nCOMMANDS - "+bcolors.ENDC) 60 | for i in commands: 61 | if i==":login" or i==":register" or i==":quitchat": 62 | continue 63 | print(bcolors.OKGREEN+i+" --> "+bcolors.BOLD+commands[i]+bcolors.ENDC) 64 | 65 | print(bcolors.OKGREEN+"Hold Ctrl+C to terminate the program anytime you want.\n"+bcolors.ENDC) 66 | 67 | 68 | def show_online(): 69 | try: 70 | data_to_send = "{'cmd':'showonline'}" 71 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 72 | signature = encryption.signature(data_to_send,"keypriv") 73 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 74 | sock.send(outp) 75 | 76 | except: 77 | print(bcolors.FAIL+"Couldn't communicate with the server :("+bcolors.ENDC) 78 | return 0 79 | 80 | 81 | def register(): 82 | while True: 83 | uname = raw_input(bcolors.OKBLUE+"Choose a username : "+bcolors.ENDC) 84 | passwd = getpass.getpass(bcolors.OKBLUE+"Enter Password : "+bcolors.ENDC) 85 | retype_passwd = getpass.getpass(bcolors.OKBLUE+"Re-type Password : "+bcolors.ENDC) 86 | 87 | if(passwd==retype_passwd): 88 | break 89 | else: 90 | print(bcolors.FAIL+"Passwords donot match, try again."+bcolors.ENDC) 91 | try: 92 | data_to_send = "{'cmd':'register','uname':'%s','passwd':'%s'}"%(uname,passwd) 93 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 94 | signature = encryption.signature(data_to_send,"keypriv") 95 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 96 | 97 | sock.send(outp) 98 | global username 99 | username = uname 100 | except: 101 | print(bcolors.FAIL+"Couldn't communicate with the server :("+bcolors.ENDC) 102 | return 0 103 | 104 | try: 105 | data = sock.recv(1024) 106 | except: 107 | print(bcolors.FAIL+"No response received from the server :("+bcolors.ENDC) 108 | return 0 109 | 110 | data = ast.literal_eval(data.encode("utf-8")) 111 | cipher = data["cipher"] 112 | signature = data["signature"] 113 | resp="" 114 | resp_type="" 115 | 116 | hex_decode = codecs.getdecoder("hex") 117 | cipher = hex_decode(cipher)[0] 118 | signature = hex_decode(signature)[0] 119 | 120 | f = open("serverkey.pem", "rb") 121 | publickey = f.read() 122 | f.close() 123 | 124 | #check authenticity now 125 | resp = encryption.decrypt(cipher,"keypriv.pem") 126 | authenticated = encryption.check_authenticity(resp,signature,publickey) 127 | 128 | if(authenticated==1): 129 | #authentication successful 130 | print("Autheticity verified") 131 | elif(authenticated==0): 132 | print(bcolors.FAIL+"Authenticity of the message can't be verified!"+bcolors.ENDC) 133 | return 0 134 | 135 | resp = ast.literal_eval(resp.encode()) 136 | resp_type = resp["resp_type"] 137 | 138 | if resp_type=="SUCC": 139 | global username 140 | username = uname 141 | print(bcolors.OKGREEN+"Logged in as "+bcolors.BOLD+username+bcolors.ENDC) 142 | return 1 143 | 144 | elif resp_type=="FAIL": 145 | print(bcolors.FAIL+"Can't register, try another username!"+bcolors.ENDC) 146 | return 0 147 | 148 | return 1 149 | 150 | 151 | 152 | def login(): 153 | uname = raw_input(bcolors.OKBLUE+"Enter username : "+bcolors.ENDC) 154 | passwd = getpass.getpass(bcolors.OKBLUE+"Enter Password : "+bcolors.ENDC) 155 | try: 156 | data_to_send = "{'cmd':'login','uname':'%s','passwd':'%s'}"%(uname,passwd) 157 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 158 | signature = encryption.signature(data_to_send,"keypriv") 159 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 160 | sock.sendall(outp) 161 | except Exception as e: 162 | print(bcolors.FAIL+"An error occured :("+bcolors.ENDC) 163 | print(e) 164 | return 0 165 | 166 | try: 167 | data = sock.recv(1024) 168 | except: 169 | print(bcolors.FAIL+"No response received from the server :("+bcolors.ENDC) 170 | return 0 171 | 172 | data = ast.literal_eval(data.encode("utf-8")) 173 | cipher = data["cipher"] 174 | signature = data["signature"] 175 | resp="" 176 | resp_type="" 177 | 178 | hex_decode = codecs.getdecoder("hex") 179 | cipher = hex_decode(cipher)[0] 180 | signature = hex_decode(signature)[0] 181 | 182 | 183 | f = open("serverkey.pem","r") 184 | publickey = f.read() 185 | f.close() 186 | #check authenticity now 187 | resp = encryption.decrypt(cipher,"keypriv.pem") 188 | authenticated = encryption.check_authenticity(resp,signature,publickey) 189 | 190 | if(authenticated==1): 191 | #authentication successful 192 | pass 193 | elif(authenticated==0): 194 | print(bcolors.FAIL+"Authenticity of the message can't be verified!"+bcolors.ENDC) 195 | return 0 196 | 197 | resp = ast.literal_eval(resp.encode()) 198 | resp_type = resp["resp_type"] 199 | 200 | if resp_type=="SUCC": 201 | clear_screen() 202 | global username 203 | username = uname 204 | print(bcolors.OKGREEN+"Logged in as "+bcolors.BOLD+username+bcolors.ENDC) 205 | return 1 206 | 207 | elif resp_type=="FAIL": 208 | print(bcolors.FAIL+"Can't log in!"+bcolors.ENDC) 209 | return 0 210 | 211 | def send(): 212 | while True: 213 | inp = print_to_screen(2,bcolors.OKBLUE,">>") 214 | if(not(inp=="" or inp==None)): 215 | if not(inp in commands): 216 | print_to_screen(1,bcolors.FAIL,"Invalid command!") 217 | continue 218 | 219 | elif inp==":chat": 220 | global username 221 | if(username==None): 222 | print_to_screen(1,bcolors.FAIL,"User is not logged in, or not properly configured!") 223 | sys.exit(0) 224 | 225 | from_uname = username 226 | to_uname = print_to_screen(2,bcolors.OKBLUE,"Enter username to chat with >>") 227 | msg = "talk." 228 | 229 | try: 230 | data_to_send = "{'cmd':'msg','from_uname':'%s','to_uname':'%s','msg':'%s'}"%(from_uname,to_uname,msg) 231 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 232 | signature = encryption.signature(data_to_send,"keypriv") #sign with client's private key 233 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 234 | sock.send(outp) 235 | except Exception as e: 236 | print_to_screen(1,bcolors.FAIL,"Couldn't communicate with the server!") 237 | print(e) 238 | 239 | #open new chat window now 240 | startchat(2,to_uname,msg) 241 | 242 | elif inp==":showonline": 243 | show_online() 244 | 245 | 246 | elif inp==":logout": 247 | global username 248 | try: 249 | data_to_send = "{'cmd':'logout','uname':'%s'}"%(username) 250 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 251 | signature = encryption.signature(data_to_send,"keypriv") 252 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 253 | sent = sock.send(outp) 254 | 255 | if(not(sent==0)): 256 | print(bcolors.OKGREEN+"Logged out succesfully!"+bcolors.ENDC) 257 | else: 258 | print(bcolors.FAIL+"Can't logout"+bcolors.ENDC) 259 | 260 | except Exception as e: 261 | print_to_screen(1,bcolors.FAIL,"Couldn't communicate with the server!") 262 | print(e) 263 | 264 | else: 265 | try: 266 | data_to_send = "{'cmd':'%s'}"%inp[1:len(inp)] 267 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 268 | signature = encryption.signature(data_to_send,"keypriv") 269 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 270 | sent = sock.send(outp) 271 | except: 272 | print_to_screen(1,bcolors.FAIL,"Couldn't communicate with the server!") 273 | 274 | else: 275 | continue 276 | 277 | 278 | 279 | def new_chat(platform,uname,msg,op): 280 | #open a new chat window 281 | path = os.path.abspath("messenger_client.py") 282 | 283 | if(platform.upper()=="WINDOWS"): 284 | lastindex = len(path) - path[::-1].index("\\") - 1 285 | path = path[0:lastindex+1] 286 | path = "new_chat.py" 287 | os.system("start /wait cmd /c python %s %d %s %s"%(path,op,uname,msg)) 288 | 289 | elif(platform.upper()=="DARWIN"): 290 | #MACOS 291 | lastindex = len(path) - path[::-1].index("/") - 1 292 | path = path[0:lastindex+1] 293 | path = path + "new_chat.py" 294 | appscript.app('Terminal').do_script('python %s %d %s %s'%(path,op,uname,msg)) 295 | 296 | elif(platform.upper()=="LINUX"): 297 | lastindex = len(path) - path[::-1].index("/") - 1 298 | path = path[0:lastindex+1] 299 | path = path + "new_chat.py" 300 | subprocess.call(['gnome-terminal', '-x', 'python %s %d %s %s'%(path,op,uname,msg)]) 301 | 302 | else: 303 | print_to_screen(1,bcolors.FAIL,"Your platform is not recognized, sorry!") 304 | return 305 | 306 | 307 | 308 | def startchat(op,rec_uname,msg): 309 | global chats 310 | if(rec_uname in chats): 311 | c_destination = chats[rec_uname] 312 | c_destination.send(msg) #send message to chat window 313 | 314 | else: 315 | #open new chat window to show messages 316 | sys_platform = platform.system() 317 | thr = threading.Thread(target=new_chat,args=((sys_platform,rec_uname,msg,op))) 318 | thr.daemon = True 319 | thr.start() 320 | print_to_screen(1,bcolors.OKBLUE,"Chat window opened!\n") 321 | 322 | 323 | 324 | def start_chat_thread(c,a): 325 | global chats 326 | while True: 327 | try: 328 | data = c.recv(1024) 329 | print(data) 330 | if(not(data) or data.encode("utf-8")==":quitchat"): 331 | for i in chats: 332 | if chats[i]==c: 333 | del chats[i] 334 | quitchat(i) 335 | break 336 | print_to_screen(1,bcolors.FAIL,"Chat window closed!") 337 | return 338 | 339 | else: 340 | data = ast.literal_eval(data.encode("utf-8")) 341 | if("init_uname" in data): 342 | #if username exits in chat dictionary, it means the chat window is open 343 | rec_uname = data["init_uname"] 344 | chats[rec_uname] = c 345 | 346 | else: 347 | #just sending messages 348 | print("Message : ",data) 349 | #forward msg to server 350 | rec_uname= data['rec_uname'] 351 | msg = data['msg'] 352 | sendmessage(msg,rec_uname) 353 | 354 | except: 355 | print_to_screen(1,bcolors.FAIL,"Chat window closed") 356 | for i in chats: 357 | if chats[i]==c: 358 | del chats[i] 359 | quitchat(i) 360 | break 361 | return 362 | 363 | 364 | def sendmessage(msg,rec_uname): 365 | data_to_send = "{'cmd':'msg','from_uname':'%s','to_uname':'%s','msg':'%s'}"%(username,rec_uname,msg) 366 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 367 | signature = encryption.signature(data_to_send,"keypriv") 368 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 369 | sock.send(outp) 370 | 371 | 372 | def quitchat(rec_uname): 373 | global username 374 | data_to_send = "{'cmd':'quitchat','rec_uname':'%s','from_uname':'%s'}"%(rec_uname,username) 375 | cipher = encryption.encrypt(data_to_send,"serverkey.pem",publickey=None) #encrypt with server's public key 376 | signature = encryption.signature(data_to_send,"keypriv") 377 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 378 | sock.send(outp) 379 | 380 | 381 | def listen(): 382 | while True: 383 | try: 384 | data = sock.recv(1024) 385 | if not(data): 386 | print_to_screen(1,bcolors.FAIL,"Connection terminated by the server :(") 387 | return 1 388 | 389 | data = ast.literal_eval(data.encode("utf-8")) 390 | cipher = data["cipher"] 391 | signature = data["signature"] 392 | hex_decode = codecs.getdecoder("hex") 393 | cipher = hex_decode(cipher)[0] 394 | signature = hex_decode(signature)[0] 395 | resp="" 396 | resp_type="" 397 | 398 | f = open("serverkey.pem", "rb") 399 | publickey = f.read() 400 | f.close() 401 | 402 | #check authenticity now 403 | resp = encryption.decrypt(cipher,"keypriv.pem") #decrypt with private key 404 | authenticated = encryption.check_authenticity(resp,signature,publickey) #public key of server 405 | 406 | if(authenticated==1): 407 | #authentication successful 408 | pass 409 | elif(authenticated==0): 410 | print(bcolors.FAIL+"Authenticity of the message can't be verified!"+bcolors.ENDC) 411 | return 0 412 | 413 | resp = ast.literal_eval(resp.encode("utf-8")) 414 | 415 | resp_type = resp["resp_type"] 416 | 417 | if(resp_type=="FAIL"): 418 | err_msg = resp["resp"] 419 | print_to_screen(1,bcolors.FAIL,"An Error Occured - "+bcolors.OKBLUE+"%s"%(err_msg)) 420 | 421 | elif(resp_type=="SUCC"): 422 | succ_msg = resp["resp"] 423 | clear_screen() 424 | print_to_screen(1,bcolors.OKGREEN,"Success - "+bcolors.OKBLUE+"%s"%(succ_msg)) 425 | print("\n") 426 | 427 | elif(resp_type=="msg"): 428 | global chats 429 | from_uname = resp["from_uname"] 430 | msg = resp["msg"] 431 | 432 | time.sleep(1) 433 | 434 | if(from_uname in chats): 435 | startchat(1,from_uname,msg) 436 | else: 437 | print_to_screen(1,bcolors.OKBLUE,"A new message received from %s, hit enter to see"%from_uname) 438 | open_msg = print_to_screen(2,bcolors.OKBLUE,"Open?(Y/N) : ") 439 | while True: 440 | if(open_msg.upper()=="Y"): 441 | startchat(1,from_uname,msg) 442 | break 443 | elif(open_msg.upper()=="N"): 444 | break 445 | else: 446 | print_to_screen(1,bcolors.FAIL,"Invalid option, please enter again.") 447 | 448 | elif(resp_type=="users"): 449 | online_users = resp["resp"] 450 | print_to_screen(1,bcolors.OKBLUE,"\nONLINE USERS : \n"+bcolors.ENDC) 451 | users = "" 452 | for i in online_users: 453 | if(i==""): 454 | continue 455 | users = users + str(i) + "\n" 456 | print_to_screen(1,bcolors.FAIL,users) 457 | 458 | elif(resp_type=="quitchat"): 459 | uname = resp["resp"] 460 | if(uname in chats): 461 | c_destination = chats[uname] 462 | c_destination.send(":quitchat") 463 | 464 | 465 | else: 466 | pass 467 | 468 | 469 | except KeyboardInterrupt: 470 | print(bcolors.FAIL+"Connection closed by user."+bcolors.ENDC) 471 | return 1 472 | sys.exit(0) 473 | break 474 | 475 | def handshake(): 476 | #First generate and send the public key to the server 477 | publickey = encryption.genkeys("keypub","keypriv") 478 | sock.send(publickey) 479 | #Now listen for the public key from the server 480 | publickey = sock.recv(2048) 481 | 482 | f = open("serverkey.pem","w") 483 | f.write(publickey) 484 | f.close() 485 | 486 | if(len(publickey)!=0): 487 | return 1 488 | else: 489 | return 0 490 | 491 | 492 | def manage_chat_threads(): 493 | while True: 494 | try: 495 | c1,a1 = server_sock.accept() 496 | chat_thread = threading.Thread(target=start_chat_thread,args=(c1,a1)) 497 | chat_thread.daemon = True 498 | chat_thread.start() 499 | except KeyboardInterrupt: 500 | print(bcolors.OKBLUE+"\nProgram terminated by the user, see you again :)"+bcolors.ENDC) 501 | sys.exit(0) 502 | 503 | 504 | def start_client(): 505 | clear_screen() 506 | print(bcolors.OKBLUE+"Trying to connect and handshake with the server..."+bcolors.ENDC) 507 | 508 | try: 509 | server_sock.bind(('127.0.0.1',10000)) 510 | server_sock.listen(10) 511 | except Exception as e: 512 | print(bcolors.FAIL+"Couldn't start the client!"+bcolors.ENDC) 513 | print(e) 514 | sys.exit(0) 515 | 516 | try: 517 | addr = sys.argv[1] 518 | sock.connect((addr,1560)) 519 | handshake_status = handshake() 520 | if(handshake_status==1): 521 | clear_screen() 522 | print(bcolors.OKGREEN + "Connected to messenger server succesfully :)" + bcolors.ENDC) 523 | elif(handshake_status==0): 524 | print(bcolors.FAIL+"Couldn't connect to messenger server!"+ bcolors.ENDC) 525 | sys.exit(1) 526 | except Exception as e: 527 | print(bcolors.FAIL+"Couldn't connect to messenger server!"+ bcolors.ENDC) 528 | print(e) 529 | sys.exit(1) 530 | 531 | print(bcolors.OKBLUE+"\nCOMMANDS - "+bcolors.ENDC) 532 | print(bcolors.OKGREEN+":login"+bcolors.BOLD+" -login to exisitng account"+bcolors.ENDC) 533 | print(bcolors.OKGREEN+":register"+bcolors.BOLD+" -create an account"+bcolors.ENDC) 534 | 535 | print("\n") 536 | 537 | while True: 538 | cmd = raw_input(bcolors.OKBLUE+"Enter a command >> "+bcolors.ENDC) 539 | if(cmd==":login"): 540 | logged_in = login() 541 | if(logged_in==1): 542 | clear_screen() 543 | global username 544 | print(bcolors.OKGREEN+"Logged in as "+bcolors.BOLD+username+bcolors.ENDC) 545 | break 546 | else: 547 | print("Not logged in yet") 548 | 549 | elif(cmd==":register"): 550 | registered = register() 551 | if(registered==1): 552 | clear_screen() 553 | global username 554 | print(bcolors.OKGREEN+"Logged in as "+bcolors.BOLD+username+bcolors.ENDC) 555 | break 556 | else: 557 | print("Not logged in yet") 558 | 559 | else: 560 | print(bcolors.FAIL+"Not a valid command!"+bcolors.ENDC) 561 | continue 562 | 563 | 564 | #Two threads - 565 | #one thread to listen for data 566 | #another thread to send data 567 | 568 | show_commands_menu() 569 | thr1 = threading.Thread(target=listen) 570 | thr2 = threading.Thread(target=send) 571 | 572 | thr1.daemon = True 573 | thr2.daemon = True 574 | thr1.start() 575 | thr2.start() 576 | 577 | 578 | #there is no need to use a new thread here, instead manage_chat_threads can be directly run without starting a child thread. But, since the 579 | #keyboardinterrupt is not being caught properly in windows, I had to use a thread here, and then later handle keyboardinterrupt with an 580 | #infinite while loop that only stops when keyboardinterrupt is captured 581 | 582 | chat_thread_manager = threading.Thread(target=manage_chat_threads) 583 | chat_thread_manager.daemon = True 584 | chat_thread_manager.start() 585 | 586 | while True: 587 | try: 588 | time.sleep(2) 589 | except KeyboardInterrupt: 590 | print(bcolors.FAIL+"Program terminated by user, see you again :("+bcolors.ENDC) 591 | sys.exit() 592 | 593 | 594 | if __name__ == "__main__": 595 | start_client() 596 | 597 | -------------------------------------------------------------------------------- /messenger_client/new_chat.py: -------------------------------------------------------------------------------- 1 | #new_chat.py 2 | from __future__ import print_function 3 | import sys 4 | import socket 5 | import threading 6 | import os 7 | import time 8 | 9 | if os.name=="nt": 10 | import keyboard 11 | else: 12 | from pynput.keyboard import Key, Listener 13 | 14 | class bcolors: 15 | HEADER = '\033[95m' 16 | OKBLUE = '\033[94m' 17 | OKGREEN = '\033[92m' 18 | WARNING = '\033[93m' 19 | FAIL = '\033[91m' 20 | ENDC = '\033[0m' 21 | BOLD = '\033[1m' 22 | UNDERLINE = '\033[4m' 23 | 24 | sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 25 | recepient_username = None 26 | taking_input = False 27 | 28 | 29 | def on_press(key): 30 | pass 31 | 32 | def on_release(key): 33 | try: 34 | if key==key.ctrl or key==key.ctrl_l or key==key.ctrl_r: 35 | global taking_input 36 | if(taking_input==False): 37 | res = sendmessage() 38 | if(taking_input==True): 39 | pass 40 | if(res==1): 41 | return False 42 | except: 43 | pass 44 | 45 | 46 | def hotkey(): 47 | #Thread that listens for the ctrl key 48 | listener = Listener(on_press=on_press,on_release=on_release) 49 | listener.daemon = True 50 | listener.start() 51 | 52 | 53 | def new_chat(op,rec_uname,msg): 54 | print(bcolors.OKGREEN+"Chatting with %s\n"%rec_uname+bcolors.ENDC) 55 | print(bcolors.OKBLUE+"To write a message, press Ctrl. After writing, hit Enter to send it"+bcolors.ENDC) 56 | 57 | print(bcolors.OKBLUE+"Type :quitchat to quit the chat.\n"+bcolors.ENDC) 58 | print(bcolors.WARNING+"Warning : Chat windows can open even if the user is not online, watch the main window to see if the user is online or not.\n"+bcolors.ENDC) 59 | 60 | op = int(op) 61 | if(op==1): 62 | print(bcolors.OKGREEN+rec_uname+bcolors.BOLD+" says hello"+bcolors.ENDC) 63 | #print(bcolors.OKGREEN+rec_uname+":~ "+msg+bcolors.ENDC) 64 | elif(op==2): 65 | print(bcolors.OKBLUE+"YOU:~ hello"+bcolors.ENDC) 66 | 67 | global recepient_username 68 | recepient_username = rec_uname 69 | sock.send("{'init_uname':'%s'}"%rec_uname) 70 | listen() 71 | 72 | 73 | def sendmessage(): 74 | global taking_input 75 | taking_input = True 76 | inp = raw_input(bcolors.OKBLUE+"Write your message now\n"+bcolors.ENDC) 77 | inp.replace("'"," ") 78 | inp.replace(":"," ") 79 | inp.replace('"',' ') 80 | if(inp=="" or inp==None): 81 | print("No message sent") 82 | elif(inp==":quitchat"): 83 | print(bcolors.OKBLUE+"Quitting chat...") 84 | sock.send(":quitchat") 85 | sock.close() 86 | return 1 87 | 88 | else: 89 | outp = "{'rec_uname':'%s','msg':'%s'}"%(recepient_username,inp) 90 | try: 91 | sock.send(outp) 92 | except: 93 | print(bcolors.FAIL+"Chat window can't run!"+bcolors.ENDC) 94 | sys.exit(0) 95 | 96 | sys.stdout.write("\033[F") #back to previous line 97 | sys.stdout.write("\033[K") #clear line 98 | sys.stdout.write("\033[F") #back to previous line 99 | sys.stdout.write("\033[K") #clear line 100 | print(bcolors.OKBLUE+bcolors.BOLD+"YOU:~ "+bcolors.OKBLUE+inp+bcolors.ENDC) 101 | taking_input = False 102 | return 0 103 | 104 | 105 | def listen(): 106 | while True: 107 | data = sock.recv(1024) 108 | if(not(data)): 109 | print(bcolors.FAIL+"Chat window can't run!"+bcolors.ENDC) 110 | sys.exit(0) 111 | 112 | if(data.encode("utf-8")==":quitchat"): 113 | print("\n"+bcolors.OKBLUE+recepient_username+" has left the chat!"+bcolors.ENDC) 114 | time.sleep(2) 115 | sock.close() 116 | sys.exit(0) 117 | 118 | global taking_input 119 | 120 | if(taking_input==True): 121 | sys.stdout.write("\033[F") #back to previous line 122 | sys.stdout.write("\033[K") #clear the current line 123 | #print the msg, display >> on next line and stay on same line 124 | print(bcolors.OKGREEN+recepient_username+":~ "+data.encode("utf-8")+bcolors.ENDC+bcolors.OKBLUE+"\nWrite your message now\n ",end="") 125 | else: 126 | #print msg normally 127 | print(bcolors.OKGREEN+bcolors.BOLD+recepient_username+":~ "+data.encode("utf-8")+bcolors.ENDC) 128 | 129 | 130 | if __name__=="__main__": 131 | os.system('cls' if os.name == 'nt' else 'clear') 132 | try: 133 | sock.connect(('127.0.0.1',10000)) #dont change these values 134 | except: 135 | print(bcolors.FAIL+"Chat window cannot be opened!"+bcolors.ENDC) 136 | sys.exit(0) 137 | if(len(sys.argv)==4): 138 | #listen for hot keys 139 | 140 | if os.name=='nt': 141 | shortcut = 'ctrl' #define your hot-key 142 | keyboard.add_hotkey(shortcut, sendmessage) #<-- attach the function to hot-key 143 | else: 144 | hotkey()#use pynput for mac and linux 145 | 146 | new_chat(sys.argv[1],sys.argv[2],sys.argv[3]) 147 | else: 148 | print("This script must not be run manually, it is used by the main script to open new chat windows!") 149 | -------------------------------------------------------------------------------- /messenger_server/encryption.py: -------------------------------------------------------------------------------- 1 | import Crypto 2 | from Crypto.PublicKey import RSA 3 | from Crypto import Random 4 | from Crypto.Hash import SHA256 5 | from Crypto.Signature import PKCS1_v1_5 6 | from Crypto.PublicKey import RSA 7 | from Crypto.Cipher import PKCS1_OAEP 8 | import ast 9 | import hashlib 10 | import codecs 11 | from base64 import ( 12 | b64encode, 13 | b64decode, 14 | ) 15 | 16 | 17 | 18 | def genkeys(public_fname,private_fname): 19 | random_generator = Random.new().read 20 | key = RSA.generate(1024, random_generator) #generate pub and priv key 21 | public = key.publickey().exportKey('PEM').decode('ascii') 22 | private = key.exportKey('PEM').decode('ascii') 23 | f = open(private_fname+".pem","w") 24 | f.write(private) 25 | f.close() 26 | f = open(public_fname+".pem","w") 27 | f.write(public) 28 | f.close() 29 | return public 30 | 31 | 32 | def encrypt(text,fname,publickey): 33 | if(publickey==None): 34 | publickey = RSA.importKey(open(fname, "rb")) 35 | else: 36 | publickey = RSA.importKey(publickey) 37 | 38 | 39 | hexify = codecs.getencoder("hex") 40 | encrypted = publickey.encrypt(text, 32) 41 | encrypted2 = hexify(encrypted[0])[0] 42 | return encrypted2 43 | 44 | def decrypt(cipher,fname): 45 | privatekey = RSA.importKey(open(fname, "rb")) 46 | decrypted = privatekey.decrypt(str(cipher)) 47 | return decrypted 48 | 49 | 50 | def hash_string(text): 51 | digest = SHA256.new() 52 | digest.update(text) 53 | return digest 54 | 55 | def signature(text,private_fname): 56 | digest = SHA256.new() 57 | digest.update(text) 58 | privatekey = RSA.importKey(open(private_fname+".pem", "rb")) 59 | signer = PKCS1_v1_5.new(privatekey) 60 | sig = signer.sign(digest) 61 | hexify = codecs.getencoder("hex") 62 | sig = hexify(sig)[0] 63 | return sig 64 | 65 | 66 | def check_authenticity(text,signature,publickey): 67 | publickey = RSA.importKey(publickey.encode()) 68 | verifier = PKCS1_v1_5.new(publickey) 69 | digest = SHA256.new() 70 | digest.update(text) 71 | verified = verifier.verify(digest, signature) 72 | 73 | if(verified): 74 | return 1 75 | else: 76 | return 0 77 | 78 | -------------------------------------------------------------------------------- /messenger_server/messenger_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | import ast 4 | import sqlite3 5 | import os.path 6 | from os import path 7 | import codecs 8 | import encryption 9 | import sys 10 | 11 | sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 12 | 13 | sock.bind(('',1560)) 14 | sock.listen(10) 15 | 16 | connections = list() 17 | authorized_users = dict() 18 | client_keys= dict() 19 | 20 | 21 | def makedb(): 22 | if(path.exists("users.db")): 23 | return 1 24 | else: 25 | conn = sqlite3.connect('users.db') 26 | conn.execute("CREATE TABLE users(id INTEGER PRIMARY KEY, username varchar2(10),password varchar2(200))") 27 | 28 | 29 | def showusers(): 30 | print("Registered Users - ") 31 | conn = sqlite3.connect('users.db') 32 | cursor = conn.execute("select * from users") 33 | for i in cursor: 34 | print(i[0]) 35 | print(i[1]) 36 | print(i[2]) 37 | print("\n") 38 | 39 | 40 | 41 | def showonlineusers(c): 42 | online_users = list() 43 | 44 | #authorized_users contains all the users that are active now 45 | for i in authorized_users: 46 | online_users.append(i) 47 | 48 | outp = "{'resp_type':'users', 'resp':%s}"%str(online_users) 49 | 50 | send(outp,c) 51 | 52 | 53 | def send(resp,c): 54 | #encrypts and sends 55 | data_to_send = resp 56 | publickey = client_keys[c] 57 | cipher = encryption.encrypt(data_to_send,fname=None,publickey=publickey) #encrypt with client's public key 58 | signature = encryption.signature(data_to_send,"server_keypriv") 59 | outp = "{'cipher':'%s','signature':'%s'}"%(cipher,signature) 60 | sent = c.send(outp) 61 | 62 | if(sent==0): 63 | print(bcolors.FAIL+"Can't send the data, connection closed by client"+bcolors.ENDC) 64 | return 0 65 | 66 | 67 | def login(uname,passwd,c): 68 | print("logging in") 69 | conn = sqlite3.connect('users.db') 70 | try: 71 | cursor = conn.execute("SELECT username,password from users where username='%s' and password='%s'"%(uname,passwd)) 72 | rowcount = len(cursor.fetchall()) 73 | if(rowcount==1): 74 | #login success 75 | if not(uname in authorized_users): 76 | print("Login success") 77 | #c.send("SUCC:User succesfully authorized!") 78 | outp = "{'resp_type':'SUCC','resp':'User succesfully authorized!'}" 79 | send(outp,c) 80 | authorized_users[uname] = c 81 | else: 82 | print("User already logged in") 83 | #c.send("FAIL:User already logged in") 84 | outp = "{'resp_type':'FAIL','resp':'User already logged in'}" 85 | send(outp,c) 86 | 87 | elif(rowcount==0): 88 | #login fail 89 | print("Login fail") 90 | #c.send("FAIL:Wrong username/password") 91 | outp = "{'resp_type':'FAIL','resp':'Wrong username or password'}" 92 | send(outp,c) 93 | 94 | except Exception as e: 95 | print("Error - %s"%e) 96 | #c.send("FAIL:%s"%e) 97 | outp = "{'resp_type':'FAIL','resp':'%s'}"%e 98 | send(outp,c) 99 | c.close() 100 | 101 | 102 | def register(uname,passwd,c): 103 | conn = sqlite3.connect('users.db') 104 | try: 105 | cursor = conn.execute("SELECT username from users where username='%s'"%(uname)) 106 | rowcount = len(cursor.fetchall()) 107 | print("Number of usrs with the username %s : "%uname,rowcount) 108 | if(rowcount==1): 109 | #User exists 110 | outp = "{'resp_type':'FAIL','resp':'Username already exists'}" 111 | send(outp,c) 112 | elif(rowcount==0): 113 | #Username available 114 | conn.execute("INSERT INTO users(username,password) values('%s','%s')"%(uname,passwd)) 115 | conn.commit() 116 | conn.close() 117 | authorized_users[uname] = c 118 | print("User created!") 119 | outp = "{'resp_type':'SUCC','resp':'User created!'}" 120 | send(outp,c) 121 | 122 | 123 | 124 | except Exception as e: 125 | print("Error - %s"%e) 126 | outp = "{'resp_type':'FAIL','resp':'%s'}"%e 127 | send(outp,c) 128 | c.close() 129 | 130 | 131 | def sendmessage(from_uname,to_uname,msg): 132 | 133 | #first check if the to_uname is online 134 | if not(to_uname in authorized_users): 135 | #User is not online 136 | outp = "{'resp_type':'FAIL','resp':'User doesnot exist or is not online!'}" 137 | send(outp,c) 138 | 139 | else: 140 | print("Authorized users : ",str(authorized_users)) 141 | c_destination = authorized_users[to_uname] 142 | outp = "{'resp_type':'msg','from_uname':'%s','msg':'%s'}"%(from_uname,msg) 143 | send(outp,c_destination) 144 | 145 | 146 | def quitchat(rec_uname,from_uname): 147 | c_destination = authorized_users[rec_uname] 148 | outp = "{'resp_type':'quitchat','resp':'%s'}"%from_uname 149 | send(outp,c_destination) 150 | 151 | def logout(uname): 152 | if(uname in authorized_users): 153 | del authorized_users[uname] 154 | outp = "{'resp_type':'SUCC','resp':'User logged out succesfully'}" 155 | send(outp,c) 156 | else: 157 | outp = "{'resp_type':'FAIL','resp':'User already logged out!'}" 158 | send(outp,c) 159 | 160 | 161 | def new_connection(c,a): 162 | #Accept data from the client 163 | while True: 164 | try: 165 | data = c.recv(1024) 166 | except Exception as e: 167 | print("Error - %s"%e) 168 | 169 | if(not(data)): 170 | print("Connection closed by client.\n") 171 | del connections[connections.index(c)] 172 | for i in authorized_users: 173 | if authorized_users[i]==c: 174 | del authorized_users[i] 175 | break 176 | break 177 | 178 | else: 179 | try: 180 | 181 | if "BEGIN PUBLIC KEY" in data: 182 | #handshaking stage 183 | client_keys[c] = data.encode() #add client public key to dictionary 184 | print("Handshaking..") 185 | f = open("server_keypriv.pem","rb") 186 | server_publickey = f.read() 187 | f.close() 188 | c.send(server_publickey) 189 | print("Sent public key") 190 | 191 | else: 192 | #print("\nData received : %s\n"%data) 193 | data = ast.literal_eval(data) 194 | #first check authenticity 195 | cipher = data["cipher"] 196 | signature = data["signature"] 197 | publickey = client_keys[c].encode() 198 | decode_hex = codecs.getdecoder("hex") 199 | signature = decode_hex(signature)[0] 200 | cipher = decode_hex(cipher)[0] 201 | #check authenticity now 202 | req = encryption.decrypt(cipher,"server_keypriv.pem") 203 | publickey = client_keys[c] 204 | authenticated = encryption.check_authenticity(req,signature,publickey)#public key of client 205 | req = ast.literal_eval(req.encode("utf-8")) 206 | if(authenticated==1): 207 | #authentication successful 208 | cmd = req['cmd'] 209 | 210 | if(cmd=='login'): 211 | login(req['uname'],req['passwd'],c) 212 | 213 | elif(cmd=='register'): 214 | register(req['uname'],req['passwd'],c) 215 | 216 | elif(cmd=='msg'): 217 | sendmessage(req['from_uname'],req['to_uname'],req['msg']) 218 | 219 | elif(cmd=='showonline'): 220 | showonlineusers(c) 221 | 222 | elif(cmd=='logout'): 223 | logout(req["uname"]) 224 | 225 | elif(cmd=='quitchat'): 226 | rec_uname = req["rec_uname"] 227 | from_uname = req["from_uname"] 228 | #we need to tell rec_uname that from_uname has left the chat 229 | for i in authorized_users: 230 | if i==rec_uname: 231 | quitchat(i,from_uname) 232 | 233 | else: 234 | outp = "{'resp_type':'FAIL','resp':'Invalid command'}" 235 | send(outp,c) 236 | 237 | elif(authenticated==0): 238 | print(bcolors.FAIL+"Authenticity of the message can't be verified!"+bcolors.ENDC) 239 | 240 | except Exception as e: 241 | print("Wrong format.") 242 | print(e) 243 | 244 | 245 | 246 | if __name__ == "__main__": 247 | makedb() 248 | encryption.genkeys("server_keypub","server_keypriv") 249 | #showusers() 250 | 251 | while True: 252 | c,a = sock.accept() 253 | connections.append(c) 254 | thr = threading.Thread(target=new_connection,args=(c,a)) 255 | thr.daemon = True 256 | thr.start() 257 | --------------------------------------------------------------------------------