Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SP-CRA security solutions #331

Merged
merged 20 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6706600
Added 2_0 folder for MS 2.0
SelinaTII Sep 18, 2023
0525b29
Added 2_0 folder for MS 2.0
SelinaTII Sep 18, 2023
445b80d
Added folder for jamming
SelinaTII Sep 19, 2023
a54694e
Added the script performing Signal Processing assisted Authentication…
anshul-tii Oct 2, 2023
95b6360
Improved script with bug fixes and resolving the issue of infinite ex…
anshul-tii Nov 6, 2023
58f7cca
Functional and regression test cases for the Signal processing assist…
anshul-tii Nov 6, 2023
fceb884
Adding Basic performance test Cases for the SP-CRA Implementation
anshul-tii Nov 6, 2023
8f21dd2
Adding relevant security test cases for SP-CRA Implementation
anshul-tii Nov 6, 2023
02ca7a7
Minor Comments formating
anshul-tii Nov 6, 2023
474b17f
Added following changes: sendall() is used instead of send, fix the …
anshul-tii Nov 8, 2023
d2aba4e
Resolved space issue to maintian uniformity in code pointed by Mika
anshul-tii Nov 8, 2023
2bcd76d
Merge branch 'develop' into PHYSEC_SOLN
anshul-tii Nov 9, 2023
128d1c4
Modified the script to comply with the main script. THe entry point o…
anshul-tii Nov 17, 2023
c394ef1
main.py script acting as an entry point for all securiy features. THe…
anshul-tii Nov 17, 2023
36a7899
updating branch
anshul-tii Nov 17, 2023
0e75f1e
RSS Authentication Scripts
anshul-tii Nov 21, 2023
8fde56c
Modified the RSS_Auth Class implementation to do away with passing s…
anshul-tii Nov 21, 2023
ba1beff
Modified the main.py script and the feature.yaml file to consider RSS…
anshul-tii Nov 21, 2023
1193a9d
Added a debug flag option to control the disply of authentication tab…
anshul-tii Nov 21, 2023
afc25c7
Making all feature disbaled by default in accordance to Sebastien com…
anshul-tii Nov 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/sc-mesh-secure-deployment/src/2_0/features.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
PHY: true
IDS: false
jamming: false

Original file line number Diff line number Diff line change
Expand Up @@ -2,131 +2,271 @@
import threading
import random
import pickle
#import scipy.io
from mat4py import loadmat
import time
import logging
import numpy as np
import os
from getmac import get_mac_address # <-- Import the get_mac_address function
import queue
from getmac import get_mac_address
from mat4py import loadmat

class PHYCRA:
def __init__(self):
logging.basicConfig(level=logging.INFO)
logging.getLogger().setLevel(logging.INFO)
# Initialization code
log_filename = '/tmp/server_log.log' # Log file location
txt_log_filename = '/tmp/server_log.txt'
# Clear server log at the start of each session
if os.path.exists(log_filename):
os.remove(log_filename)
if os.path.exists(txt_log_filename):
os.remove(txt_log_filename)


logging.basicConfig(level=logging.INFO, filename=log_filename, filemode='w',
format='%(asctime)s - %(levelname)s - %(message)s')
logging.info("Server initialized")

# Server setup
DataServer = loadmat('ACF_Table.mat')
self.acf = np.array(DataServer['y_C']).transpose()
#self.acf = DataServer['y_C'].transpose()
self.SERVER = self.get_server_ip()
self.BROADCAST_PORT = 5051
self.PORT = 5050
self.ADDR = (self.SERVER, self.PORT)
self.FORMAT = 'utf-8'

# Determine the directory in which this script is located
script_dir = os.path.dirname(os.path.realpath(__file__))

# Use the script's directory to construct the path to the .mat file
mat_file_path = os.path.join(script_dir, 'ACF_Table.mat')
try:
DataServer = loadmat(mat_file_path)
self.acf = np.array(DataServer['y_C']).transpose()
self.SERVER = self.get_server_ip()
self.BROADCAST_PORT = 5051
self.PORT = 5050
self.ADDR = (self.SERVER, self.PORT)
self.FORMAT = 'utf-8'
logging.info("Server setup completed successfully")
except Exception as e:
logging.error("Error during server setup: %s", e)

# Client setup
DataClient = loadmat('ACF_Table.mat')
self.acf_client = np.array(DataClient['y_C']).transpose()
#self.acf_client = DataClient['y_C'].transpose()

# Clear the server log at the start of each session
if os.path.exists("server_log.txt"):
os.remove("server_log.txt")

# Start server and client functionalities
server_thread = threading.Thread(target=self.server_start)
server_thread.start()
time.sleep(2)
listen_thread = threading.Thread(target=self.listen_for_broadcast)
listen_thread.start()

# SERVER FUNCTIONS
try:
DataClient = loadmat(mat_file_path)
self.acf_client = np.array(DataClient['y_C']).transpose()
logging.info("Client setup completed successfully")
except Exception as e:
logging.error("Error during client setup: %s", e)

# Initialize threads but do not start them
self.server_thread = None
self.listen_thread = None
self.broadcast_thread = None
self.results_queue = {'Pass': [], 'Fail': []}
#self.all_attempts = set() # Set to track all unique attempts

# Create an event for graceful shutdown
self.stop_event = threading.Event()

def start(self):
"""Starts the server and client functionalities in separate threads."""
try:
self.server_thread = threading.Thread(target=self.server_start)
self.server_thread.start()
logging.info("Server thread started")

self.listen_thread = threading.Thread(target=self.listen_for_broadcast)
self.listen_thread.start()
logging.info("Broadcast listening thread started")
except Exception as e:
logging.error("Error during server/client threads initialization: %s", e)

def stop(self):
"""Stops all running threads and closes sockets."""
self.stop_event.set()

if self.server_thread and self.server_thread.is_alive():
self.server_thread.join()
if self.listen_thread and self.listen_thread.is_alive():
self.listen_thread.join()
if self.broadcast_thread and self.broadcast_thread.is_alive():
self.broadcast_thread.join()

if hasattr(self, 'server') and self.server:
self.server.close()
if hasattr(self, 'listen_sock') and self.listen_sock:
self.listen_sock.close()
logging.info("PHYCRA stopped successfully")



def log_authentication(self, node_ip, mac_address, result):
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"{timestamp}\t{node_ip}\t{mac_address}\t{result}\n"
with open("server_log.txt", "a") as log_file:
with open("/tmp/server_log.txt", "a") as log_file:
log_file.write(log_entry)


def display_table(self):
logging.info("Displaying authentication table")
print("+---------------------+---------------+-------------------+---------------------------+")
print("| Time | Node IP | MAC Address | Authentication Result |")
print("+---------------------+---------------+-------------------+---------------------------+")
anshul-tii marked this conversation as resolved.
Show resolved Hide resolved
with open("server_log.txt", "r") as log_file:
for line in log_file:
timestamp, node_ip, mac_address, result = line.strip().split("\t")
formatted_line = f"| {timestamp:<19} | {node_ip:<12} | {mac_address:<15} | {result:<23} |"
print(formatted_line)
log_file_path = "/tmp/server_log.txt"
if os.path.exists(log_file_path):
with open(log_file_path, "r") as log_file:
for line in log_file:
timestamp, node_ip, mac_address, result = line.strip().split("\t")
formatted_line = f"| {timestamp:<19} | {node_ip:<12} | {mac_address:<15} | {result:<23} |"
print(formatted_line)
else:
print("No entries found.")
print("+---------------------+---------------+-------------------+---------------------------+")


def handle_client(self, conn, addr):
print(f"Connection request received from {addr}")
index = random.randint(0, len(self.acf) - 1)
acf_tx = pickle.dumps(self.acf[index])
conn.send(acf_tx)

# Receive and convert rx_index to int
rx_index_length = int.from_bytes(conn.recv(2), 'big')
rx_index = int(conn.recv(rx_index_length).decode(self.FORMAT))

# Then receive MAC address
mac_address_length = int.from_bytes(conn.recv(2), 'big')
mac_address = conn.recv(mac_address_length).decode(self.FORMAT)

if rx_index == index:
print("Node is authenticated")
self.log_authentication(addr[0], mac_address, "Success")
else:
print('Access denied')
self.log_authentication(addr[0], mac_address, "Access denied")
print("\nUpdated Table:")
self.display_table()
conn.close()
"""
Handles the client connection, sends a challenge (ACF value),
and verifies the response from the client to authenticate.
It also logs the authentication result.
"""
try:
# Sending a random ACF value as a challenge to the client
index = random.randint(0, len(self.acf) - 1)
acf_tx = pickle.dumps(self.acf[index])
conn.sendall(acf_tx)
# Receive and verify the index from the client
rx_index_length_bytes = conn.recv(2)
rx_index_length = int.from_bytes(rx_index_length_bytes, 'big')
rx_index_bytes = conn.recv(rx_index_length)
rx_index = int(rx_index_bytes.decode(self.FORMAT))

# Then receive the MAC address from the client
mac_address_length_bytes = conn.recv(2)
mac_address_length = int.from_bytes(mac_address_length_bytes, 'big')
mac_address_bytes = conn.recv(mac_address_length)
mac_address = mac_address_bytes.decode(self.FORMAT)
# Authenticate the client based on the index and MAC address received
if rx_index == index:
print("PASS: Authentication successful")
logging.info("Authentication successful for %s", addr)
self.log_authentication(addr[0], mac_address, "Success")
self.results_queue['Pass'].append((addr[0], mac_address))
else:
print('FAIL: Authentication failed')
logging.warning("Authentication failed for %s", addr)
self.log_authentication(addr[0], mac_address, "Access denied")
self.results_queue['Fail'].append((addr[0], mac_address))

print("\nUpdated Table:")
self.display_table()
except Exception as e:
logging.error("Error during client handling: %s", e)
finally:
conn.close()

def get_result(self):
"""
Retrieve and clear the latest results from the results queue.
"""
current_results = {k: list(v) for k, v in self.results_queue.items()}
self.results_queue = {'Pass': [], 'Fail': []} # Reset for next batch
return current_results


def server_start(self):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(self.ADDR)
server.listen()
broadcast_thread = threading.Thread(target=self.broadcast_status)
broadcast_thread.start()
while True:
conn, addr = server.accept()
thread = threading.Thread(target=self.handle_client, args=(conn, addr))
thread.start()
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind(self.ADDR)
self.server.listen()
self.server.settimeout(1)
logging.info("Server started and listening")
except Exception as e:
logging.error("Error during server binding/listening: %s", e)
return

try:
self.broadcast_thread = threading.Thread(target=self.broadcast_status)
self.broadcast_thread.start()
logging.info("Broadcast thread started")
while not self.stop_event.is_set(): # Check if the stop event is set
try:
conn, addr = self.server.accept()
except socket.timeout:
continue
thread = threading.Thread(target=self.handle_client, args=(conn, addr))
thread.start()
except Exception as e:
logging.error("Error during server operations: %s", e)
finally:
self.server.close()
logging.info("Server shutdown")


def broadcast_status(self):
broadcast_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
broadcast_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
while True:
msg = "SERVER_AVAILABLE"
broadcast_sock.sendto(msg.encode(), ('<broadcast>', self.BROADCAST_PORT))
time.sleep(60)
try:
broadcast_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
broadcast_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
while not self.stop_event.is_set(): # Check if the stop event is set
msg = "SERVER_AVAILABLE"
broadcast_sock.sendto(msg.encode(), ('<broadcast>', self.BROADCAST_PORT))
time.sleep(20)
except Exception as e:
logging.error("Error during broadcast: %s", e)
finally:
broadcast_sock.close()
logging.info("Broadcast stopped")


# CLIENT FUNCTIONS

def get_mac_address(self):
return get_mac_address()


def connect_to_server(self, server_ip):
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((server_ip, self.PORT))
acf_rx = pickle.loads(client.recv(4096))
index = str(np.where((self.acf_client == acf_rx).all(axis=1))[0][0])
client.send(len(index).to_bytes(2, 'big')) # Send length of rx_index first
client.send(index.encode(self.FORMAT)) # Send rx_index
mac_address = self.get_mac_address()
client.send(len(mac_address).to_bytes(2, 'big')) # Send length of MAC address
client.send(mac_address.encode(self.FORMAT)) # Then send MAC address
client.close()

"""
Connects to the server, receives a challenge (ACF value),
calculates the index of the received ACF value in the local acf_client table,
sends back this index and the MAC address to the server for authentication.
"""
try:
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((server_ip, self.PORT))
# Receive the ACF challenge from the server
acf_rx = pickle.loads(client.recv(4096))
# Calculate the index of the received ACF
index = str(np.where((self.acf_client == acf_rx).all(axis=1))[0][0])
# Send the calculated index back to the server
index_bytes = index.encode(self.FORMAT)
client.sendall(len(index_bytes).to_bytes(2, 'big')) # Send length of rx_index first
client.sendall(index_bytes) # Send rx_index
# Retrieve the local MAC address and send it to the server
mac_address = self.get_mac_address()
mac_address_bytes = mac_address.encode(self.FORMAT)
client.sendall(len(mac_address_bytes).to_bytes(2, 'big')) # Send length of MAC address first
client.sendall(mac_address_bytes) # Then send MAC address
logging.info("Successfully sent index and MAC address to the server")
except Exception as e:
logging.error("Error during connection to server: %s", e)
finally:
client.close()


def listen_for_broadcast(self):
listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
listen_sock.bind(('', self.BROADCAST_PORT))
while True:
data, addr = listen_sock.recvfrom(1024)
if data.decode() == "SERVER_AVAILABLE" and addr[0] != self.SERVER:
self.connect_to_server(addr[0])
try:
self.listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.listen_sock.bind(('', self.BROADCAST_PORT))
self.listen_sock.settimeout(1)
logging.info("Listening for broadcasts")
while not self.stop_event.is_set():
try:
data, addr = self.listen_sock.recvfrom(1024)
except socket.timeout:
continue
if data.decode() == "SERVER_AVAILABLE" and addr[0] != self.SERVER:
self.connect_to_server(addr[0])
except Exception as e:
logging.error("Error during broadcast reception: %s", e)
finally:
self.listen_sock.close()
logging.info("Stopped listening for broadcasts")


# Common Functions
def get_server_ip(self):
Expand All @@ -140,5 +280,3 @@ def get_server_ip(self):
s.close()
return IP

if __name__ == "__main__":
phycra = PHYCRA()
Loading
Loading