I have a simple modbus client-server code and also have a diffie-hellman client-server key exchange. My aim is to integrate the two so that when modbus client initiates request to modbus server, first step is do a key exchange and then encrypt the data transmission from modbus client to server and vice versa.
I know that in python socketserver the class BaseRequestHandler
the handle()
method is overridden for communication between client and server which I have done for diffie-hellman key exchange.
dh-server.py
import socketserver
# request handler
class Dh_Handler(socketserver.BaseRequestHandler):
# instantiate request handler, one per connection
def __init__(self, request, client_addr, server):
self.params = load_dh_params() # store DH parameters
self.state = 0 # tracking state
socketserver.BaseRequestHandler.__init__(self, request, client_addr, server) # pass variables to BaseRequestHandler
# must override handle() to implement communication to client
def handle(self):
# calculate shared secret
shared_key = dh_key_exchange()
# key derivation used for symmetric encryption
key = key_derivation(shared_key)
def main():
host, port = '10.8.8.11', 502
# instance of TCP server class, params: ip_address, request_handler
dh_server = socketserver.TCPServer((host, port), Dh_Handler)
# stop server via keyboard interrupt
try:
# infinite loop listen
dh_server.serve_forever()
except KeyboardInterrupt:
dh_server.shutdown()
dh-client.py
import socket
def main():
# we specify the server's address or hostname and port
host, port = '10.8.8.11', 502
# create a tcp socket for IPv4
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect to the tcp socket
sock.connect((host, port))
# key exchange from server
shared_key = dh_key_exchange(sock)
# key derivation for symmetric encryption
key = key_derivation(shared_key)
modbus-client.py
import socket
from umodbus import conf
from umodbus.client import tcp
import binascii
# Enable values to be signed (default is False).
conf.SIGNED_VALUES = True
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('10.8.8.11', 502))
# Returns a message or Application Data Unit (ADU) specific for doing
# Modbus TCP/IP.
message = tcp.write_multiple_coils(slave_id=1, starting_address=1, values=[1, 0, 1, 0])
# Response depends on Modbus function code. This particular returns the
# amount of coils written, in this case it is.
response = tcp.send_message(message, sock)
message = tcp.read_coils(slave_id=1, starting_address=1, quantity=4)
# message=b"".join([message,b'\x64'])
print(binascii.b2a_hex(message))
responce = tcp.send_message(message, sock)
print(responce)
sock.close()
modbus-server.py
import logging
from socketserver import TCPServer
from collections import defaultdict
from umodbus import conf
from umodbus.server.tcp import RequestHandler, get_server
from umodbus.utils import log_to_stream
# Add stream handler to logger 'uModbus'.
log_to_stream(level=logging.DEBUG)
# A very simple data store which maps addresses against their values.
data_store = defaultdict(int)
# Enable values to be signed (default is False).
conf.SIGNED_VALUES = True
TCPServer.allow_reuse_address = True
app = get_server(TCPServer, ('10.8.8.11', 502), RequestHandler)
@app.route(slave_ids=[1], function_codes=[1, 2], addresses=list(range(0, 10)))
def read_data_store(slave_id, function_code, address):
"""" Return value of address. """
return data_store[address]
@app.route(slave_ids=[1], function_codes=[5, 15], addresses=list(range(0, 10)))
def write_data_store(slave_id, function_code, address, value):
"""" Set value for address. """
data_store[address] = value
if __name__ == '__main__':
try:
app.serve_forever()
finally:
app.shutdown()
app.server_close()
What would be the best way to approach this? Can I create a server that listens on two different ports? One for key exchange and the other for encrypted modbus transmission or use threading in socketserver. What about using selectors to handle each connection.
Any help is much appreciated.