Update Flowchart.vsd, app_secure_client.py, and 2 more files...Work in Progress

This commit is contained in:
George 2020-03-29 23:28:41 -07:00
parent 5531da077f
commit d55f387c8c
4 changed files with 163 additions and 54 deletions

Binary file not shown.

View File

@ -13,7 +13,9 @@ import socket
import json import json
import io import io
import struct import struct
import hashlib
from Crypto import Random
from Crypto.PublicKey import RSA
def create_request(action, value): def create_request(action, value):
if action == "search": if action == "search":
@ -105,6 +107,55 @@ def process_response(sock, addr, jsonheader, _recv_buffer):
print(f"got response: {repr(content)}") print(f"got response: {repr(content)}")
def send_client_key(sock, data):
response = {
"content_bytes": data,
"content_type": "binary/custom-server-binary-type",
"content_encoding": "binary",
}
message = _create_message(**response)
print(f'Sending client public key and hash...')
sock.sendall(message)
def recv_server_secrets(sock, _recv_buffer, key):
HDRLEN = 2
# read and process protoheader (fixed length of 2 byte in network order)
read_to_buffer(sock, HDRLEN, _recv_buffer)
_jsonheader_len = struct.unpack(">H", _recv_buffer[:HDRLEN])[0]
_recv_buffer = _recv_buffer[HDRLEN:]
# read and process jsonheader
read_to_buffer(sock, _jsonheader_len, _recv_buffer)
jsonheader = _json_decode(_recv_buffer[:_jsonheader_len], "utf-8")
_recv_buffer = _recv_buffer[_jsonheader_len:]
for reqhdr in ("byteorder", "content-length", "content-type",
"content-encoding",):
if reqhdr not in jsonheader:
raise ValueError(f'Missing required header "{reqhdr}".')
content_len = jsonheader["content-length"]
read_to_buffer(sock, content_len, _recv_buffer)
data = _recv_buffer[:content_len]
_recv_buffer = _recv_buffer[content_len:]
sesskeyhash = data[-128:]
data = data[:-128]
encryptedsess = data[-256:]
# halfsesskey = key.decrypt(encryptedsess)
data = data[:-256]
pubkeyhash = data[-128:]
pubkey = data[:-128]
if hashlib.sha3_512(pubkey).hexdigest().encode() != pubkeyhash:
sock.shutdown(socket.SHUT_RDWR)
sock.close()
raise Exception('Server public key does not match hash')
if hashlib.sha3_512(halfsesskey).hexdigest().encode() != sesskeyhash:
sock.shutdown(socket.SHUT_RDWR)
sock.close()
raise Exception('Server public key does not match hash')
print(f"Received server secrets correctly.")
return pubkey, halfsesskey
def main(): def main():
HDRLEN = 2 HDRLEN = 2
_recv_buffer = bytearray(b"") _recv_buffer = bytearray(b"")
@ -114,15 +165,23 @@ def main():
sys.exit(1) sys.exit(1)
host, port = sys.argv[1], int(sys.argv[2]) host, port = sys.argv[1], int(sys.argv[2])
action, value = sys.argv[3], sys.argv[4]
request = create_request(action, value)
message = create_message(request)
addr = (host, port) addr = (host, port)
print("starting connection to", addr) print("starting connection to", addr)
sock = socket.create_connection(addr) sock = socket.create_connection(addr)
sock.settimeout(5) sock.settimeout(10)
# security handshake
key = RSA.generate(2048, Random.new().read)
publickey = key.publickey().exportKey('PEM')
keyhash = hashlib.sha3_512(publickey).hexdigest().encode()
data = publickey + keyhash
send_client_key(sock, data)
# receiving server secrets - public key and session key
serverkey, sessionkey = recv_server_secrets(sock, _recv_buffer, key)
action, value = sys.argv[3], sys.argv[4]
request = create_request(action, value)
message = create_message(request)
print(f"sending request {repr(message)} to {addr}") print(f"sending request {repr(message)} to {addr}")
sock.sendall(message) sock.sendall(message)

View File

@ -15,34 +15,36 @@ import lib_secure_server as libserver
sel = selectors.DefaultSelector() sel = selectors.DefaultSelector()
def accept_wrapper(sock): def accept_wrapper(sock, passhash):
conn, addr = sock.accept() # Should be ready to read conn, addr = sock.accept() # Should be ready to read
print("accepted connection from", addr) print("accepted connection from", addr)
conn.setblocking(False) conn.setblocking(False)
message = libserver.Message(sel, conn, addr) message = libserver.Message(sel, conn, addr, passhash)
sel.register(conn, selectors.EVENT_READ, data=message) sel.register(conn, selectors.EVENT_READ, data=message)
def main():
if len(sys.argv) != 3: if len(sys.argv) != 3:
print("usage:", sys.argv[0], "<host> <port>") print("usage:", sys.argv[0], "<host> <port>")
sys.exit(1) sys.exit(1)
with open('password.txt', 'r') as f:
passhash = f.read()
passhash = passhash.encode() # into byte string
host, port = sys.argv[1], int(sys.argv[2])
lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Avoid bind() exception: OSError: [Errno 48] Address already in use
lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
lsock.bind((host, port))
lsock.listen()
print("listening on", (host, port))
lsock.setblocking(False)
sel.register(lsock, selectors.EVENT_READ, data=None)
host, port = sys.argv[1], int(sys.argv[2]) try:
lsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Avoid bind() exception: OSError: [Errno 48] Address already in use
lsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
lsock.bind((host, port))
lsock.listen()
print("listening on", (host, port))
lsock.setblocking(False)
sel.register(lsock, selectors.EVENT_READ, data=None)
try:
while True: while True:
events = sel.select(timeout=None) events = sel.select(timeout=None)
for key, mask in events: for key, mask in events:
if key.data is None: if key.data is None:
accept_wrapper(key.fileobj) accept_wrapper(key.fileobj, passhash)
else: else:
message = key.data message = key.data
try: try:
@ -53,8 +55,11 @@ try:
f"{message.addr}:\n{traceback.format_exc()}", f"{message.addr}:\n{traceback.format_exc()}",
) )
message.close() message.close()
except KeyboardInterrupt: except KeyboardInterrupt:
print("caught keyboard interrupt, exiting") print("caught keyboard interrupt, exiting")
finally: finally:
sel.close() sel.close()
lsock.close() lsock.close()
if __name__ == '__main__':
main()

View File

@ -9,7 +9,12 @@ import sys
import selectors import selectors
import json import json
import io import io
import os
import struct import struct
import hashlib
from Crypto import Random
from Crypto.PublicKey import RSA
request_search = { request_search = {
"morpheus": "Follow the white rabbit. \U0001f430", "morpheus": "Follow the white rabbit. \U0001f430",
@ -19,7 +24,7 @@ request_search = {
class Message: class Message:
def __init__(self, selector, sock, addr): def __init__(self, selector, sock, addr, passhash):
self.selector = selector self.selector = selector
self.sock = sock self.sock = sock
self.addr = addr self.addr = addr
@ -29,6 +34,16 @@ class Message:
self.jsonheader = None self.jsonheader = None
self.request = None self.request = None
self.response_created = False self.response_created = False
# added for security
self.key = RSA.generate(2048, Random.new().read)
self.sessionkey = os.urandom(8)
self.passhash = passhash # pass phrase hash
self.clientkey = None # client public key
self.handshake = False
print(f"initiating message associated with {addr}")
def __del__(self):
print(f"destructing messange associlated with {self.addr}")
def _set_selector_events_mask(self, mode): def _set_selector_events_mask(self, mode):
"""Set selector to listen for events: mode is 'r', 'w', or 'rw'.""" """Set selector to listen for events: mode is 'r', 'w', or 'rw'."""
@ -120,6 +135,14 @@ class Message:
} }
return response return response
def _create_handshake_content(self, data):
response = {
"content_bytes": data,
"content_type": "binary/custom-server-binary-type",
"content_encoding": "binary",
}
return response
def process_events(self, mask): def process_events(self, mask):
if mask & selectors.EVENT_READ: if mask & selectors.EVENT_READ:
self.read() self.read()
@ -148,7 +171,7 @@ class Message:
self._write() self._write()
def close(self): def close(self):
print("closing connection to", self.addr) print(f"closing connection to {self.addr}\n")
try: try:
self.selector.unregister(self.sock) self.selector.unregister(self.sock)
except Exception as e: except Exception as e:
@ -192,12 +215,34 @@ class Message:
if reqhdr not in self.jsonheader: if reqhdr not in self.jsonheader:
raise ValueError(f'Missing required header "{reqhdr}".') raise ValueError(f'Missing required header "{reqhdr}".')
def send_my_secrets(self):
halfsesskey = self.sessionkey
encryptedsess = self.clientkey.encrypt(halfsesskey, None)[0]
sesskeyhash = hashlib.sha3_512(halfsesskey).hexdigest().encode()
mypubkey = self.key.publickey().exportKey()
mypubkeyhash = hashlib.sha3_512(mypubkey).hexdigest().encode()
mysecret = mypubkey + mypubkeyhash + encryptedsess + sesskeyhash
response = self._create_handshake_content(mysecret)
message = self._create_message(**response)
self._send_buffer += message
print(f'Sending server public key, hash, session key, hash...')
def process_request(self): def process_request(self):
content_len = self.jsonheader["content-length"] content_len = self.jsonheader["content-length"]
if not len(self._recv_buffer) >= content_len: if not len(self._recv_buffer) >= content_len:
return return
data = self._recv_buffer[:content_len] data = self._recv_buffer[:content_len]
self._recv_buffer = self._recv_buffer[content_len:] self._recv_buffer = self._recv_buffer[content_len:]
if not self.handshake: # data is client public key plus hash
key = data[:-128]
keyhash = data[-128:]
if hashlib.sha3_512(key).hexdigest().encode() == keyhash:
self.clientkey = RSA.importKey(key)
else:
self.sock.close()
raise Exception('Client plublic key does not match key hash')
self.send_my_secrets()
else:
if self.jsonheader["content-type"] == "text/json": if self.jsonheader["content-type"] == "text/json":
encoding = self.jsonheader["content-encoding"] encoding = self.jsonheader["content-encoding"]
self.request = self._json_decode(data, encoding) self.request = self._json_decode(data, encoding)