Feature/py iot (#4)
* Changing files but having issues with space tabs. * Removed config. Fixed indents. * Removed config. * Fixed test config. Fixed syntax in cli. * Editing cli and hub to finally commit transactions. * Added better logging logic. Will clean up in the morning. * Hub is more functional. * Edited IoT to gen keys. * Public keys are now generated, sent, and stored. However, they are not transmitted when a new client joins. * Added crypto testing * Communication is now functional.
This commit is contained in:
parent
5d0a26ca7e
commit
a85b08f050
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,6 +3,7 @@ __pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
config.yaml
|
config.yaml
|
||||||
|
config.yml
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
55
src/cipher.py
Normal file
55
src/cipher.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
from random import random
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def __init__(self, ws):
|
||||||
|
self.new = True
|
||||||
|
self.ws = ws
|
||||||
|
self.n = None
|
||||||
|
self.e = None
|
||||||
|
|
||||||
|
|
||||||
|
def log_prefix(t, client=None):
|
||||||
|
if client is None:
|
||||||
|
return str(time.time()) + ' [' + t + ':srv] '
|
||||||
|
return str(time.time()) + ' [' + t + ':' + str(client['id']) + '] '
|
||||||
|
|
||||||
|
|
||||||
|
def load_msg(message):
|
||||||
|
try:
|
||||||
|
msg = json.loads(message)
|
||||||
|
except:
|
||||||
|
msg = message
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def load_pub_key(cli, msg):
|
||||||
|
try:
|
||||||
|
cli.n = msg['pub_key']['n']
|
||||||
|
cli.e = msg['pub_key']['e']
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
print('\tMessage had no attribute pub_key. Failed.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def gen_comm_msg(to, ident, msg):
|
||||||
|
message = {
|
||||||
|
'type':'message',
|
||||||
|
'to': to,
|
||||||
|
'id': ident,
|
||||||
|
'contents': msg
|
||||||
|
}
|
||||||
|
return json.dumps(message)
|
||||||
|
|
||||||
|
|
||||||
|
def gen_pub_key_msg(n, e):
|
||||||
|
message = {
|
||||||
|
'type': 'pub_key',
|
||||||
|
'pub_key':{
|
||||||
|
'n':n,
|
||||||
|
'e':e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json.dumps(message)
|
162
src/cli.py
Normal file
162
src/cli.py
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
"""
|
||||||
|
Python client based on https://pypi.org/project/websocket-client/
|
||||||
|
|
||||||
|
@author hornetfighter515
|
||||||
|
@author Todorov-Lachezar
|
||||||
|
"""
|
||||||
|
|
||||||
|
import websocket
|
||||||
|
import _thread
|
||||||
|
import time
|
||||||
|
import ssl
|
||||||
|
import yaml
|
||||||
|
import json
|
||||||
|
import rsa
|
||||||
|
import random # for message id generation
|
||||||
|
import cipher
|
||||||
|
|
||||||
|
ext = False
|
||||||
|
authed = False
|
||||||
|
clis = []
|
||||||
|
messages = {}
|
||||||
|
|
||||||
|
def authed_ext_send(ws, msg):
|
||||||
|
msg = rsa.encrypt(msg.encode(), clis[0]).decode('latin')
|
||||||
|
message_id = random.random()
|
||||||
|
if len(clis) <= 1: # if there are no IoTs
|
||||||
|
# send the message once-encrypted
|
||||||
|
message = cipher.gen_comm_msg(0, message_id, msg)
|
||||||
|
ws.send(message)
|
||||||
|
return
|
||||||
|
# otherwise, slice and twice encrypt it
|
||||||
|
twice_es = []
|
||||||
|
modifier = len(msg)/(len(clis)-1)
|
||||||
|
for i in range(1,len(clis)):
|
||||||
|
start = int( (i-1) * modifier)
|
||||||
|
end = int(i*modifier)
|
||||||
|
# makes a slice of the encrypted message, and indicates the intended
|
||||||
|
# recipient
|
||||||
|
contents = rsa.encrypt(msg[start:end].encode('latin'),
|
||||||
|
clis[i]).decode('latin')
|
||||||
|
message = cipher.gen_comm_msg(i, message_id, contents)
|
||||||
|
twice_es.append(message)
|
||||||
|
for m in twice_es:
|
||||||
|
ws.send(m)
|
||||||
|
|
||||||
|
|
||||||
|
def authed_int_send(ws, msg):
|
||||||
|
to = int(input(f"Who's it to? 0-{len(clis)}>"))
|
||||||
|
e_msg = rsa.encrypt(msg.encode(), clis[int(to)]).decode('latin')
|
||||||
|
message_id = random.random()
|
||||||
|
msg = cipher.gen_comm_msg(to, message_id, e_msg)
|
||||||
|
ws.send(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def init_send(ws):
|
||||||
|
message = cipher.gen_pub_key_msg(pub['n'], pub['e'])
|
||||||
|
ws.send(message)
|
||||||
|
|
||||||
|
|
||||||
|
def send(ws, msg):
|
||||||
|
try:
|
||||||
|
if ext:
|
||||||
|
authed_ext_send(ws, msg)
|
||||||
|
else:
|
||||||
|
authed_int_send(ws, msg)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Sending message {msg} failed: {e}')
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(ws, message):
|
||||||
|
msg = json.loads(message)
|
||||||
|
global authed
|
||||||
|
if msg['type'] == 'pub_key':
|
||||||
|
authed = True
|
||||||
|
if ext:
|
||||||
|
k = rsa.PublicKey(msg['available_cli']['n'],
|
||||||
|
msg['available_cli']['e'])
|
||||||
|
if len(clis) == 0:
|
||||||
|
clis.append(k)
|
||||||
|
else:
|
||||||
|
clis[0] = k
|
||||||
|
for i,p in msg['available_iots'].items():
|
||||||
|
k = rsa.PublicKey(p['n'], p['e'])
|
||||||
|
clis.append(k)
|
||||||
|
else:
|
||||||
|
k = rsa.PublicKey(msg['available_cli']['n'],
|
||||||
|
msg['available_cli']['e'])
|
||||||
|
clis.append(k)
|
||||||
|
|
||||||
|
elif msg['type'] == 'message':
|
||||||
|
# check if it's only a partial message
|
||||||
|
try:
|
||||||
|
if 'from' in msg:
|
||||||
|
if msg['id'] not in messages:
|
||||||
|
messages[msg['id']] = {}
|
||||||
|
messages[msg['id']][msg['from']] = msg['contents']
|
||||||
|
contents = None
|
||||||
|
for i in range(0, 3):
|
||||||
|
if i in messages[msg['id']]:
|
||||||
|
if contents is None:
|
||||||
|
contents = messages[msg['id']][i].encode('latin')
|
||||||
|
else:
|
||||||
|
contents += messages[msg['id']][i].encode('latin')
|
||||||
|
if len(contents) == 256:
|
||||||
|
m = rsa.decrypt(contents, priv).decode()
|
||||||
|
else:
|
||||||
|
m = ' '
|
||||||
|
else:
|
||||||
|
contents = msg['contents'].encode('latin')
|
||||||
|
m = rsa.decrypt(contents, priv).decode()
|
||||||
|
if m is not None:
|
||||||
|
print(f"\033[F\033[1G<< {m}\n> ")
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Failed to receive message due to {e}')
|
||||||
|
|
||||||
|
|
||||||
|
def on_error(ws, error):
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
|
||||||
|
def on_close(ws, close_status_code, close_msg):
|
||||||
|
print(f"### closed. reason: {close_msg} ###")
|
||||||
|
|
||||||
|
|
||||||
|
def on_open(ws):
|
||||||
|
def run(*args):
|
||||||
|
init_send(ws)
|
||||||
|
running = True
|
||||||
|
while running:
|
||||||
|
outbound = input(">")
|
||||||
|
if outbound == 'q':
|
||||||
|
running = False
|
||||||
|
else:
|
||||||
|
send(ws, outbound)
|
||||||
|
ws.close()
|
||||||
|
_thread.start_new_thread(run, ())
|
||||||
|
|
||||||
|
|
||||||
|
def open_socket(url, port):
|
||||||
|
ws = websocket.WebSocketApp(f"ws://{url}:{port}",
|
||||||
|
on_open=on_open,
|
||||||
|
on_message=on_message,
|
||||||
|
on_error=on_error,
|
||||||
|
on_close=on_close)
|
||||||
|
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
with open('src/config.yml', 'r') as f:
|
||||||
|
conf = yaml.safe_load(f)
|
||||||
|
|
||||||
|
e = input("Are you outside of the network? (Y/n)")
|
||||||
|
if e == 'y' or e == 'Y' or e == 'yes' or e == 'Yes':
|
||||||
|
ext = True
|
||||||
|
ws_conf = conf['ws']['ext']
|
||||||
|
else:
|
||||||
|
ws_conf = conf['ws']['int']
|
||||||
|
print('Generating keys...')
|
||||||
|
pub, priv = rsa.newkeys(2048)
|
||||||
|
print('Keys generated')
|
||||||
|
open_socket(ws_conf['url'], ws_conf['port'])
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
import websocket
|
|
||||||
import _thread
|
|
||||||
import time
|
|
||||||
import ssl
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
def on_message(ws, message):
|
|
||||||
print(f"<< {message}")
|
|
||||||
|
|
||||||
def on_error(ws, error):
|
|
||||||
print(error)
|
|
||||||
|
|
||||||
def on_close(ws, close_status_code, close_msg):
|
|
||||||
print(f"### closed. reason: {close_msg} ###")
|
|
||||||
|
|
||||||
def on_open(ws):
|
|
||||||
def run(*args):
|
|
||||||
running = True
|
|
||||||
while running:
|
|
||||||
outbound = input(">")
|
|
||||||
if outbound == 'q':
|
|
||||||
running = False
|
|
||||||
else:
|
|
||||||
ws.send(outbound)
|
|
||||||
ws.close()
|
|
||||||
_thread.start_new_thread(run, ())
|
|
||||||
|
|
||||||
def open_socket(url, port):
|
|
||||||
ws = websocket.WebSocketApp(f"ws://{url}:{port}",
|
|
||||||
on_open=on_open,
|
|
||||||
on_message=on_message,
|
|
||||||
on_error=on_error,
|
|
||||||
on_close=on_close)
|
|
||||||
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
with open('config.yaml', 'r') as f:
|
|
||||||
conf = yaml.safe_load(f)
|
|
||||||
open_socket(conf['ws']['url'], conf['ws']['port'])
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
ws:
|
|
||||||
url: localhost
|
|
||||||
port: 6873
|
|
|
@ -1,35 +0,0 @@
|
||||||
"""
|
|
||||||
Encrypting and decrypting a message
|
|
||||||
|
|
||||||
https://www.geeksforgeeks.org/how-to-encrypt-and-decrypt-strings-in-python/
|
|
||||||
|
|
||||||
@author Todorov-Lachezar
|
|
||||||
"""
|
|
||||||
|
|
||||||
import rsa
|
|
||||||
|
|
||||||
class EncryptDecrypt():
|
|
||||||
|
|
||||||
def make_keys(key_length):
|
|
||||||
publicKey, privateKey = ""
|
|
||||||
# Generates public and private keys
|
|
||||||
# Method takes in key length as a parameter
|
|
||||||
# Note: the key length should be >16
|
|
||||||
if(key_length < 16):
|
|
||||||
publicKey, privateKey = rsa.newkeys(key_length)
|
|
||||||
return publicKey, privateKey
|
|
||||||
else:
|
|
||||||
return "Enter a key_length of >16"
|
|
||||||
|
|
||||||
|
|
||||||
def encryption(plaintext, publicKey):
|
|
||||||
# Encrypts the message with the public key
|
|
||||||
# Note: make sure to encode the message before encrypting
|
|
||||||
encMessage = rsa.encrypt(plaintext.encode(), publicKey)
|
|
||||||
return encMessage
|
|
||||||
|
|
||||||
def decryption(ciphertext, privateKey):
|
|
||||||
# Decrypts the encrypted message with the private key
|
|
||||||
# Note: make sure to decode the message after decryption
|
|
||||||
decMessage = rsa.decrypt(ciphertext, privateKey).decode()
|
|
||||||
return decMessage
|
|
|
@ -1,3 +0,0 @@
|
||||||
ws:
|
|
||||||
url: localhost
|
|
||||||
port: 6873
|
|
33
src/e_d_test.py
Normal file
33
src/e_d_test.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
"""
|
||||||
|
oh no
|
||||||
|
|
||||||
|
@author hornetfighter515
|
||||||
|
"""
|
||||||
|
import rsa
|
||||||
|
|
||||||
|
print('Generating keys...')
|
||||||
|
|
||||||
|
(pub_one, priv_one) = rsa.newkeys(2048)
|
||||||
|
(pub_two, priv_two) = rsa.newkeys(2048)
|
||||||
|
(pub_three, priv_three) = rsa.newkeys(2048)
|
||||||
|
|
||||||
|
print('Keys generated!')
|
||||||
|
|
||||||
|
message = 'hello world'
|
||||||
|
|
||||||
|
e_one = rsa.encrypt(message.encode(), pub_one)
|
||||||
|
e_two = rsa.encrypt(e_one[:128], pub_two)
|
||||||
|
e_three = rsa.encrypt(e_one[128:], pub_three)
|
||||||
|
|
||||||
|
print('Successfully encrypted!')
|
||||||
|
|
||||||
|
d_three = rsa.decrypt(e_three, priv_three)
|
||||||
|
d_two = rsa.decrypt(e_two, priv_two)
|
||||||
|
print(str(e_one))
|
||||||
|
print('-----------------------------')
|
||||||
|
print(str(d_two + d_three))
|
||||||
|
print(str(e_one) == str(d_two + d_three))
|
||||||
|
|
||||||
|
d_one = rsa.decrypt(d_two + d_three, priv_one)
|
||||||
|
|
||||||
|
print('Successfully decrypted ' + d_one.decode())
|
34
src/encrypt_decrypt.py
Executable file
34
src/encrypt_decrypt.py
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
"""
|
||||||
|
Encrypting and decrypting a message
|
||||||
|
|
||||||
|
https://www.geeksforgeeks.org/how-to-encrypt-and-decrypt-strings-in-python/
|
||||||
|
|
||||||
|
@author hornetfighter515
|
||||||
|
@author Todorov-Lachezar
|
||||||
|
"""
|
||||||
|
|
||||||
|
import rsa
|
||||||
|
|
||||||
|
def make_keys(key_length):
|
||||||
|
publicKey, privateKey = None,None
|
||||||
|
# Generates public and private keys
|
||||||
|
# Method takes in key length as a parameter
|
||||||
|
# Note: the key length should be >16
|
||||||
|
if(key_length > 16):
|
||||||
|
(publicKey, privateKey) = rsa.newkeys(key_length)
|
||||||
|
return publicKey, privateKey
|
||||||
|
else:
|
||||||
|
return "Enter a key_length of >16"
|
||||||
|
|
||||||
|
|
||||||
|
def encryption(plaintext, publicKey):
|
||||||
|
# Encrypts the message with the public key
|
||||||
|
# Note: make sure to encode the message before encrypting
|
||||||
|
encMessage = rsa.encrypt(plaintext.encode(), publicKey)
|
||||||
|
return encMessage
|
||||||
|
|
||||||
|
def decryption(ciphertext, privateKey):
|
||||||
|
# Decrypts the encrypted message with the private key
|
||||||
|
# Note: make sure to decode the message after decryption
|
||||||
|
decMessage = rsa.decrypt(ciphertext, privateKey).decode()
|
||||||
|
return decMessage
|
215
src/hub.py
Normal file
215
src/hub.py
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
"""
|
||||||
|
Creates hub for distributed IoT
|
||||||
|
|
||||||
|
Websocket Server docs: https://github.com/Pithikos/python-websocket-server
|
||||||
|
|
||||||
|
@author hornetfighter515
|
||||||
|
@author Lachezar Todorov
|
||||||
|
"""
|
||||||
|
|
||||||
|
import threading
|
||||||
|
from websocket_server import WebsocketServer
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
from random import random
|
||||||
|
|
||||||
|
iot_devs = {}
|
||||||
|
e_clients = {}
|
||||||
|
i_client = None
|
||||||
|
name=''
|
||||||
|
|
||||||
|
ext_srv = None
|
||||||
|
int_srv = None
|
||||||
|
iot_srv = None
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
def __init__(self, ws):
|
||||||
|
self.new = True
|
||||||
|
self.ws = ws
|
||||||
|
self.n = None
|
||||||
|
self.e = None
|
||||||
|
|
||||||
|
|
||||||
|
def _log_prefix(t, client=None):
|
||||||
|
if client is None:
|
||||||
|
return str(time.time()) + ' [' + t + ':srv] '
|
||||||
|
return str(time.time()) + ' [' + t + ':' + str(client['id']) + '] '
|
||||||
|
|
||||||
|
# #
|
||||||
|
#################### INCOMING MESSAGES ########################################
|
||||||
|
# #
|
||||||
|
|
||||||
|
def _load_msg(message):
|
||||||
|
try:
|
||||||
|
msg = json.loads(message)
|
||||||
|
except:
|
||||||
|
msg = message
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def _pub_key(cli, msg):
|
||||||
|
try:
|
||||||
|
cli.n = msg['pub_key']['n']
|
||||||
|
cli.e = msg['pub_key']['e']
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
print('\tMessage had no attribute pub_key. Failed.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _cli_pubs(coll):
|
||||||
|
res = {}
|
||||||
|
if len(coll) == 0:
|
||||||
|
return res
|
||||||
|
for c, cli in coll.items():
|
||||||
|
res[c] = {
|
||||||
|
"n":cli.n,
|
||||||
|
"e":cli.e
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def ext_msg(client, server, message):
|
||||||
|
# external coming in to network
|
||||||
|
print(_log_prefix('ext', client), 'sent message.')
|
||||||
|
cli = e_clients[client['id']]
|
||||||
|
msg = _load_msg(message)
|
||||||
|
if cli.new:
|
||||||
|
if _pub_key(cli, msg):
|
||||||
|
# give IoTs or cli
|
||||||
|
if len(iot_devs) > 0:
|
||||||
|
print('\tThere are IoTs to send to.')
|
||||||
|
else:
|
||||||
|
print('\tCommunicate directly with client.')
|
||||||
|
cli.new = False
|
||||||
|
int_srv.send_message(i_client.ws, json.dumps({
|
||||||
|
"type":"pub_key",
|
||||||
|
"available_cli":{
|
||||||
|
"n":cli.n,
|
||||||
|
"e":cli.e
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
ext_srv.send_message(client, json.dumps({
|
||||||
|
"type":"pub_key",
|
||||||
|
"name":name,
|
||||||
|
"available_cli":{
|
||||||
|
"n":i_client.n,
|
||||||
|
"e":i_client.e
|
||||||
|
},
|
||||||
|
"available_iots": _cli_pubs(iot_devs)
|
||||||
|
}))
|
||||||
|
else:
|
||||||
|
print(f'\tMessage {msg["id"]} goes to {msg["to"]}')
|
||||||
|
if msg['to'] == 0:
|
||||||
|
print('\tTo internal client')
|
||||||
|
int_srv.send_message(i_client.ws, message)
|
||||||
|
else:
|
||||||
|
iot_srv.send_message(iot_devs[msg['to']].ws, message)
|
||||||
|
print('\tTo IoT device')
|
||||||
|
|
||||||
|
|
||||||
|
def int_msg(client, server, message):
|
||||||
|
# internal leaving network
|
||||||
|
print(_log_prefix('int', client), 'Sent message.' )
|
||||||
|
msg = _load_msg(message)
|
||||||
|
if msg['type'] == 'pub_key':
|
||||||
|
if i_client.new:
|
||||||
|
if _pub_key(i_client, msg):
|
||||||
|
print(f'\tinternal client successfully authed.')
|
||||||
|
i_client.new = False
|
||||||
|
return
|
||||||
|
elif msg['type'] == 'message':
|
||||||
|
global e_clients
|
||||||
|
to = msg['to'] + 1
|
||||||
|
ext_srv.send_message(e_clients[to].ws, message)
|
||||||
|
print('\tMessage sent to external client')
|
||||||
|
|
||||||
|
|
||||||
|
def iot_msg(client, server, message):
|
||||||
|
# Partially decrypted message coming back from IoT devices
|
||||||
|
print(_log_prefix('iot', client), 'sent message.')
|
||||||
|
iot = iot_devs[client['id']]
|
||||||
|
msg = _load_msg(message)
|
||||||
|
if iot.new:
|
||||||
|
if _pub_key(iot, msg):
|
||||||
|
print(f'\tIoT {client["id"]} successfully authed.')
|
||||||
|
iot.new = False
|
||||||
|
else:
|
||||||
|
if i_client is not None:
|
||||||
|
int_srv.send_message(i_client.ws, message)
|
||||||
|
print('\tInternal client safe to send to.')
|
||||||
|
else:
|
||||||
|
print('\tInternal client disconnected.')
|
||||||
|
|
||||||
|
# #
|
||||||
|
######################### NEW CONNECTIONS #####################################
|
||||||
|
# #
|
||||||
|
|
||||||
|
def ext_new(client, server):
|
||||||
|
global e_clients
|
||||||
|
if client['id'] not in e_clients:
|
||||||
|
e_clients[client['id']] = Client(client)
|
||||||
|
print(_log_prefix('ext', client) + 'Client joined.')
|
||||||
|
# give list of iots and their pubkeys
|
||||||
|
|
||||||
|
def int_new(client, server):
|
||||||
|
print(_log_prefix('int', client) + 'Client joined.')
|
||||||
|
global i_client
|
||||||
|
if i_client is None:
|
||||||
|
i_client = Client(client)
|
||||||
|
print('\tInternal client set.')
|
||||||
|
|
||||||
|
def iot_new(client, server):
|
||||||
|
global iot_devs
|
||||||
|
if client['id'] not in iot_devs:
|
||||||
|
iot_devs[client['id']] = Client(client)
|
||||||
|
print(_log_prefix('iot', client) + 'Device joined.')
|
||||||
|
|
||||||
|
|
||||||
|
def start_server(conf, new, msg, prefix):
|
||||||
|
ws = WebsocketServer(host=conf['url'], port=conf['port'])
|
||||||
|
ws.set_fn_new_client(new)
|
||||||
|
ws.set_fn_message_received(msg)
|
||||||
|
wst = threading.Thread(target=ws.run_forever)
|
||||||
|
wst.daemon = True
|
||||||
|
wst.start()
|
||||||
|
print(_log_prefix(prefix), 'Running...')
|
||||||
|
return ws
|
||||||
|
|
||||||
|
def start_servers(ext_conf, int_conf, iot_conf):
|
||||||
|
"""
|
||||||
|
Starts a server for each type of device.
|
||||||
|
Why create a different server for each client type? Separation of concerns
|
||||||
|
is easier this way. Messages don't need to be sorted as heavily by which
|
||||||
|
client they came from. Internal ports can more easily be firewalled, or not
|
||||||
|
exposed to the outside internet at all.
|
||||||
|
@param host: the host it is being run on
|
||||||
|
@param port_e: the externally exposed port for clients outside the network
|
||||||
|
@param port_i: the internal port for clients to connect to
|
||||||
|
@param port_iot: the port for IoT devices to connect to
|
||||||
|
"""
|
||||||
|
# websocket server exposed, or external, for external clients
|
||||||
|
global ext_srv
|
||||||
|
ext_srv = start_server(ext_conf, ext_new, ext_msg, 'ext')
|
||||||
|
|
||||||
|
# websocket server internal
|
||||||
|
global int_srv
|
||||||
|
int_srv = start_server(int_conf, int_new, int_msg, 'int')
|
||||||
|
|
||||||
|
# websocket server for IoT devices
|
||||||
|
global iot_srv
|
||||||
|
iot_srv = start_server(iot_conf, iot_new, iot_msg, 'iot')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import yaml
|
||||||
|
with open('src/config.yml', 'r') as f:
|
||||||
|
conf = yaml.safe_load(f)
|
||||||
|
ext_conf = conf['ws']['ext']
|
||||||
|
int_conf = conf['ws']['int']
|
||||||
|
iot_conf = conf['ws']['iot']
|
||||||
|
start_servers(ext_conf, int_conf, iot_conf)
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
"""
|
|
||||||
Creates hub for distributed IoT
|
|
||||||
|
|
||||||
@author hornetfighter515
|
|
||||||
@author Lachezar Todorov
|
|
||||||
"""
|
|
||||||
|
|
||||||
from websocket_server import WebsocketServer
|
|
||||||
import time
|
|
||||||
|
|
||||||
class Serv():
|
|
||||||
def __init__(self, host, port):
|
|
||||||
self.host = host
|
|
||||||
self.port = port
|
|
||||||
self.ws_s = WebsocketServer(host=host, port=port)
|
|
||||||
self.ws_s.set_fn_message_received(self.message_received)
|
|
||||||
self.ws_s.set_fn_new_client(self.new_client)
|
|
||||||
self.ws_s.run_forever()
|
|
||||||
|
|
||||||
def _log_prefix(self):
|
|
||||||
return str(time.time()) + ' [' + self.host + ':' + str(self.port) + '] '
|
|
||||||
|
|
||||||
def new_client(self, client, server):
|
|
||||||
print(self._log_prefix() + 'Client joined.')
|
|
||||||
|
|
||||||
def message_received(self, client, server, message):
|
|
||||||
# figure out how to identify clients...
|
|
||||||
print(self._log_prefix() + message )
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import yaml
|
|
||||||
with open('config.yml', 'r') as f:
|
|
||||||
conf = yaml.safe_load(f)
|
|
||||||
ws_conf = conf['ws']
|
|
||||||
ext_server = Serv(ws_conf['ext']['url'], ws_conf['ext']['port'])
|
|
|
@ -1,7 +0,0 @@
|
||||||
ws:
|
|
||||||
ext:
|
|
||||||
url: localhost
|
|
||||||
port: 6873
|
|
||||||
int:
|
|
||||||
url: localhost
|
|
||||||
port: 6872
|
|
79
src/iot.py
Normal file
79
src/iot.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
"""
|
||||||
|
A middleman IoT websocket device which decrypts and re-encrypts a message
|
||||||
|
|
||||||
|
@author hornetfighter515
|
||||||
|
@author Lachezar Todorov
|
||||||
|
"""
|
||||||
|
import websocket
|
||||||
|
import _thread
|
||||||
|
import time
|
||||||
|
import yaml
|
||||||
|
import json
|
||||||
|
import rsa
|
||||||
|
|
||||||
|
|
||||||
|
def send(ws, message):
|
||||||
|
try:
|
||||||
|
ws.send(json.dumps(message))
|
||||||
|
except:
|
||||||
|
print(f'Sending of message {message} failed.')
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(ws, message):
|
||||||
|
# decrypt the message
|
||||||
|
#try:
|
||||||
|
if True:
|
||||||
|
msg = json.loads(message)
|
||||||
|
contents = msg['contents'].encode('latin')
|
||||||
|
res = {
|
||||||
|
"from":msg['to'],
|
||||||
|
"to":0,
|
||||||
|
"type":'message',
|
||||||
|
"id":msg['id'],
|
||||||
|
"contents": rsa.decrypt(contents, priv).decode('latin')
|
||||||
|
}
|
||||||
|
print('Forwarding message...')
|
||||||
|
send(ws, res)
|
||||||
|
#except:
|
||||||
|
# print('Sending failed.')
|
||||||
|
# send(ws, message)
|
||||||
|
|
||||||
|
|
||||||
|
def on_error(ws, error):
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
def on_close(ws, close_status_code, close_msg):
|
||||||
|
print(f"### closed. reason: {close_msg} ###")
|
||||||
|
|
||||||
|
def on_open(ws):
|
||||||
|
def run(*args):
|
||||||
|
send(ws, {
|
||||||
|
"pub_key":{
|
||||||
|
"n":pub.n,
|
||||||
|
"e":pub.e
|
||||||
|
}
|
||||||
|
})
|
||||||
|
running = True
|
||||||
|
while running:
|
||||||
|
time.sleep(1)
|
||||||
|
_thread.start_new_thread(run, ())
|
||||||
|
|
||||||
|
def open_socket(url, port):
|
||||||
|
ws = websocket.WebSocketApp(f"ws://{url}:{port}",
|
||||||
|
on_open=on_open,
|
||||||
|
on_message=on_message,
|
||||||
|
on_error=on_error,
|
||||||
|
on_close=on_close)
|
||||||
|
ws.run_forever()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
with open('src/config.yml', 'r') as f:
|
||||||
|
conf = yaml.safe_load(f)
|
||||||
|
global pub, priv
|
||||||
|
# eventually put this in config
|
||||||
|
print('Generating keys...')
|
||||||
|
pub, priv = rsa.newkeys(2048)
|
||||||
|
print('Keys generated!')
|
||||||
|
open_socket(conf['ws']['iot']['url'], conf['ws']['iot']['port'])
|
||||||
|
|
70
src/protocol.json
Normal file
70
src/protocol.json
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"ext-srv":{
|
||||||
|
"init":{
|
||||||
|
"name":"name",
|
||||||
|
"pub_key":"pub_key"
|
||||||
|
},
|
||||||
|
"msg":{
|
||||||
|
"id":0,
|
||||||
|
"to":0,
|
||||||
|
"contents":"contents"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"srv-ext":{
|
||||||
|
"init":{
|
||||||
|
"name":"name",
|
||||||
|
"available_cli": "public_key_zero",
|
||||||
|
"available_iots":[
|
||||||
|
"public_key_one",
|
||||||
|
"public_key_two"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"msg":{
|
||||||
|
"contents":"contents"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"int-srv":{
|
||||||
|
"init":{
|
||||||
|
"name":"name",
|
||||||
|
"pub_key":"public_key_zero"
|
||||||
|
},
|
||||||
|
"msg":{
|
||||||
|
"to":0,
|
||||||
|
"contents":"contents"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"srv-int":{
|
||||||
|
"init":{
|
||||||
|
"available_clients":[
|
||||||
|
"clients"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"msg":{
|
||||||
|
"id":0,
|
||||||
|
"position":0,
|
||||||
|
"contents":"contents"
|
||||||
|
},
|
||||||
|
"sys":{
|
||||||
|
"msg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iot-srv":{
|
||||||
|
"init":{
|
||||||
|
"id":0,
|
||||||
|
"pub_key":"public_key_one"
|
||||||
|
},
|
||||||
|
"msg":{
|
||||||
|
"id":0,
|
||||||
|
"contents":"contents"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"srv-iot":{
|
||||||
|
"init":{
|
||||||
|
"client_available":false
|
||||||
|
},
|
||||||
|
"msg":{
|
||||||
|
"id":0,
|
||||||
|
"contents":"contents"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,3 +5,6 @@ ws:
|
||||||
int:
|
int:
|
||||||
url: localhost
|
url: localhost
|
||||||
port: 6872
|
port: 6872
|
||||||
|
iot:
|
||||||
|
url: localhost
|
||||||
|
port: 6871
|
Loading…
Reference in New Issue
Block a user