diff --git a/vs/python/password_cracking_parallel/README.md b/vs/python/password_cracking_parallel/README.md new file mode 100644 index 0000000..71237a4 --- /dev/null +++ b/vs/python/password_cracking_parallel/README.md @@ -0,0 +1,35 @@ +## diff +```bash +python3 password_cracking_parallel.py +Processing number combinations concurrently +Processing 0 to 9999998 +Processing 9999999 to 19999998 +Processing 19999999 to 29999998 +Processing 29999999 to 39999998 +Processing 39999999 to 49999998 +Processing 49999999 to 59999998 +Processing 59999999 to 69999998 +Processing 69999999 to 79999998 +Processing 79999999 to 89999998 +Processing 89999999 to 99999999 +Waiting for chunks to finish +PASSWORD CRACKED: 87654321 +PROCESS TIME: 9.041890041 + + +python3.11 password_cracking_parallel.py +Processing number combinations concurrently +Processing 0 to 9999998 +Processing 9999999 to 19999998 +Processing 19999999 to 29999998 +Processing 29999999 to 39999998 +Processing 39999999 to 49999998 +Processing 49999999 to 59999998 +Processing 59999999 to 69999998 +Processing 69999999 to 79999998 +Processing 79999999 to 89999998 +Processing 89999999 to 99999999 +Waiting for chunks to finish +PASSWORD CRACKED: 87654321 +PROCESS TIME: 7.3755793329910375 +``` \ No newline at end of file diff --git a/vs/python/password_cracking_parallel/library_thread_pool.py b/vs/python/password_cracking_parallel/library_thread_pool.py new file mode 100644 index 0000000..26e792d --- /dev/null +++ b/vs/python/password_cracking_parallel/library_thread_pool.py @@ -0,0 +1,33 @@ +"""Library thread pool implementation""" + +import time +import os +from concurrent.futures import ThreadPoolExecutor, as_completed +from threading import current_thread + + +def cpu_waster(i: int) -> str: + """Wasting the processor time, professionally""" + name = current_thread().getName() + print(f"{name} doing Task {i}") + time.sleep(3) + return f"Task {i} completed" + + +def main() -> None: + with ThreadPoolExecutor( + max_workers=os.cpu_count(), + thread_name_prefix="Pool_Thread" + ) as pool: + tasks = [] + for i in range(20): + tasks.append(pool.submit(cpu_waster, i)) + + print("All work requests sent") + for task in as_completed(tasks): + print(task.result()) + print("All work completed") + + +if __name__ == "__main__": + main() diff --git a/vs/python/password_cracking_parallel/message_queue.py b/vs/python/password_cracking_parallel/message_queue.py new file mode 100644 index 0000000..b1e7aad --- /dev/null +++ b/vs/python/password_cracking_parallel/message_queue.py @@ -0,0 +1,42 @@ +"""Using message queues for IPC between threads""" + +import time +from queue import Queue +from threading import Thread, current_thread + + +class Worker(Thread): + def __init__(self, queue: Queue, id: int): + super().__init__(name=str(id)) + self.queue = queue + + def run(self) -> None: + while not self.queue.empty(): + # getting new data for processing from the queue + item = self.queue.get() + print(f"Thread {current_thread().name}: " + f"processing item {item} from the queue") + time.sleep(2) + + +def main(thread_num: int) -> None: + # creating a queue and putting integer number into it to process + q = Queue() + for i in range(10): + q.put(i) + + threads = [] + # running threads to process data from the queue + for i in range(thread_num): + thread = Worker(q, i + 1) + thread.start() + threads.append(thread) + + # block the main thread until the child threads has finished + for thread in threads: + thread.join() + + +if __name__ == "__main__": + thread_num = 4 + main(thread_num) diff --git a/vs/python/password_cracking_parallel/password_cracking_parallel.py b/vs/python/password_cracking_parallel/password_cracking_parallel.py new file mode 100644 index 0000000..3916bdf --- /dev/null +++ b/vs/python/password_cracking_parallel/password_cracking_parallel.py @@ -0,0 +1,93 @@ +"""Program for cracking the password consisting of only numbers using multi +cores in parallel""" + +import os +import math +import time +import typing as T +import hashlib +from multiprocessing import Pool + +ChunkRange = T.Tuple[int, int] + + +def get_combinations(*, length: int, min_number: int = 0, max_number: int = None) -> T.List[str]: + """Generate all possible password combinations""" + combinations = [] + if not max_number: + # calculating maximum number based on the length + max_number = int(math.pow(10, length) - 1) + + # go through all possible combinations in a given range + for i in range(min_number, max_number + 1): + str_num = str(i) + # fill in the missing numbers with zeros + zeros = "0" * (length - len(str_num)) + combinations.append("".join((zeros, str_num))) + return combinations + + +def get_crypto_hash(password: str) -> str: + """"Calculating cryptographic hash of the password""" + return hashlib.sha256(password.encode()).hexdigest() + + +def check_password(expected_crypto_hash: str, + possible_password: str) -> bool: + actual_crypto_hash = get_crypto_hash(possible_password) + # compare the resulted cryptographic hash with the one stored on the system + return expected_crypto_hash == actual_crypto_hash + + +def get_chunks(num_ranges: int, + length: int) -> T.Iterator[ChunkRange]: + """Splitting the passwords into chunks using break points""" + max_number = int(math.pow(10, length) - 1) + chunk_starts = [int(max_number / num_ranges * i) + for i in range(num_ranges)] + chunk_ends = [start_point - 1 + for start_point in + chunk_starts[1:]] + [max_number] + return zip(chunk_starts, chunk_ends) + + +def crack_chunk(crypto_hash: str, length: int, chunk_start: int, + chunk_end: int) -> T.Union[str, None]: + """Brute force the password combinations""" + print(f"Processing {chunk_start} to {chunk_end}") + combinations = get_combinations(length=length, min_number=chunk_start, + max_number=chunk_end) + for combination in combinations: + if check_password(crypto_hash, combination): + return combination # found it + return # not found + + +def crack_password_parallel(crypto_hash: str, length: int) -> None: + """Orchestrate cracking the password between different processes""" + # getting number of available processors + num_cores = os.cpu_count() + print("Processing number combinations concurrently") + start_time = time.perf_counter() + + # processing each chunk in a separate process concurrently + with Pool() as pool: + arguments = ((crypto_hash, length, chunk_start, chunk_end) for + chunk_start, chunk_end in + get_chunks(num_cores, length)) + results = pool.starmap(crack_chunk, arguments) + print("Waiting for chunks to finish") + pool.close() + pool.join() + + result = [res for res in results if res] + print(f"PASSWORD CRACKED: {result[0]}") + process_time = time.perf_counter() - start_time + print(f"PROCESS TIME: {process_time}") + + +if __name__ == "__main__": + crypto_hash = \ + "e24df920078c3dd4e7e8d2442f00e5c9ab2a231bb3918d65cc50906e49ecaef4" + length = 8 + crack_password_parallel(crypto_hash, length) diff --git a/vs/python/password_cracking_parallel/pipe.py b/vs/python/password_cracking_parallel/pipe.py new file mode 100644 index 0000000..f7f14ca --- /dev/null +++ b/vs/python/password_cracking_parallel/pipe.py @@ -0,0 +1,53 @@ +"""Using pipes for IPC between threads""" + +from threading import Thread, current_thread +from multiprocessing import Pipe +from multiprocessing.connection import Connection + + +class Writer(Thread): + """Writer thread will write messages into the pipe""" + def __init__(self, conn: Connection): + super().__init__() + self.conn = conn + self.name = "Writer" + + def run(self) -> None: + print(f"{current_thread().name}: Sending rubber duck...") + self.conn.send("Rubber duck") + + +class Reader(Thread): + """Reader thread will read messages from the pipe""" + def __init__(self, conn: Connection): + super().__init__() + self.conn = conn + self.name = "Reader" + + def run(self) -> None: + print(f"{current_thread().name}: Reading...") + msg = self.conn.recv() + print(f"{current_thread().name}: Received: {msg}") + + +def main() -> None: + # Connections for reading and writing + reader_conn, writer_conn = Pipe() + reader = Reader(reader_conn) + writer = Writer(writer_conn) + + threads = [ + writer, + reader + ] + # start threads + for thread in threads: + thread.start() + + # block the main thread until the child threads has finished + for thread in threads: + thread.join() + + +if __name__ == "__main__": + main() diff --git a/vs/python/password_cracking_parallel/shared_ipc.py b/vs/python/password_cracking_parallel/shared_ipc.py new file mode 100644 index 0000000..089c1c4 --- /dev/null +++ b/vs/python/password_cracking_parallel/shared_ipc.py @@ -0,0 +1,54 @@ +"""Using shared memory IPC between threads""" + +import time +from threading import Thread, current_thread + +SIZE = 5 +# setup shared memory +shared_memory = [-1] * SIZE + + +class Producer(Thread): + def run(self) -> None: + self.name = "Producer" + global shared_memory + for i in range(SIZE): + print(f"{current_thread().name}: Writing {int(i)}") + shared_memory[i - 1] = i + + +class Consumer(Thread): + def run(self) -> None: + self.name = "Consumer" + global shared_memory + for i in range(SIZE): + # try reading the data until succession + while True: + line = shared_memory[i] + if line == -1: + # data hasn't change - waiting for a second + print(f"{current_thread().name}: Data not available\n" + f"Sleeping for 1 second before retrying") + time.sleep(1) + continue + print(f"{current_thread().name}: Read: {int(line)}") + break + + +def main() -> None: + threads = [ + Consumer(), + Producer(), + ] + + # start threads + for thread in threads: + thread.start() + + # block the main thread until the child threads has finished + for thread in threads: + thread.join() + + +if __name__ == "__main__": + main() diff --git a/vs/python/password_cracking_parallel/sockets.py b/vs/python/password_cracking_parallel/sockets.py new file mode 100644 index 0000000..2355d5a --- /dev/null +++ b/vs/python/password_cracking_parallel/sockets.py @@ -0,0 +1,75 @@ +""" Using sockets for IPC """ + +import socket +import os.path +import time +from threading import Thread, current_thread + +# in Unix everything is a file +SOCK_FILE = "./mailbox" +BUFFER_SIZE = 1024 + + +class Sender(Thread): + def run(self) -> None: + self.name = "Sender" + # AF_UNIX (Unix domain socket) and SOCK_STREAM are constants + # that represent the socket family and socket type respectively + client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + client.connect(SOCK_FILE) + + messages = ["Hello", " ", "world!"] + for msg in messages: + print(f"{current_thread().name}: Send: '{msg}'") + client.sendall(str.encode(msg)) + + client.close() + + +class Receiver(Thread): + def run(self) -> None: + self.name = "Receiver" + server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + # bind socket to the file + server.bind(SOCK_FILE) + # let's start listening mode for this socket + server.listen() + + print(f"{current_thread().name}: Listening for incoming messages...") + # accept a connection + conn, addr = server.accept() + + while True: + # receive data from socket + data = conn.recv(BUFFER_SIZE) + if not data: + break + message = data.decode() + print(f"{current_thread().name}: Received: '{message}'") + + server.close() + + +def main() -> None: + # verify if exists the sock file + if os.path.exists(SOCK_FILE): + os.remove(SOCK_FILE) + + # receiver will create a socket and socket file + receiver = Receiver() + receiver.start() + # waiting untill the socket has been created + time.sleep(1) + sender = Sender() + sender.start() + + # block the main thread until the child threads has finished + for thread in [receiver, sender]: + thread.join() + + # cleaning up + os.remove(SOCK_FILE) + + +if __name__ == "__main__": + main() diff --git a/vs/python/password_cracking_parallel/thread_pool.py b/vs/python/password_cracking_parallel/thread_pool.py new file mode 100644 index 0000000..063db93 --- /dev/null +++ b/vs/python/password_cracking_parallel/thread_pool.py @@ -0,0 +1,73 @@ +"""Simple thread pool implementation""" + +import time +import queue +import typing as T +from threading import Thread, current_thread + +Callback = T.Callable[..., None] +Task = T.Tuple[Callback, T.Any, T.Any] +TaskQueue = queue.Queue + + +class Worker(Thread): + """Thread executing tasks from a given tasks queue""" + def __init__(self, tasks: queue.Queue[Task]): + super().__init__() + self.tasks = tasks + + def run(self) -> None: + # running the thread indefinitely + while True: + # getting the tasks from queue and execute + func, args, kargs = self.tasks.get() + try: + func(*args, **kargs) + except Exception as e: + print(e) + self.tasks.task_done() + + +class ThreadPool: + """Pool of threads consuming tasks from a queue""" + def __init__(self, num_threads: int): + # setting up the queue to put tasks + self.tasks: TaskQueue = queue.Queue(num_threads) + self.num_threads = num_threads + + # create long-running threads + for _ in range(self.num_threads): + worker = Worker(self.tasks) + worker.setDaemon(True) + worker.start() + + def submit(self, func: Callback, *args, **kargs) -> None: + """Add a task to the queue""" + self.tasks.put((func, args, kargs)) + + def wait_completion(self) -> None: + """Wait for completion of all the tasks in the queue""" + # join method that blocks the main thread until the child + # threads has finished + self.tasks.join() + + +def cpu_waster(i: int) -> None: + """Wasting the processor time, professionally""" + name = current_thread().getName() + print(f"{name} doing {i} work") + time.sleep(3) + + +def main() -> None: + pool = ThreadPool(num_threads=5) + for i in range(20): + pool.submit(cpu_waster, i) + + print("All work requests sent") + pool.wait_completion() + print("All work completed") + + +if __name__ == "__main__": + main() diff --git a/vs/python/password_cracking_sequential.py b/vs/python/password_cracking_sequential.py index 8aa10e3..d8b3543 100644 --- a/vs/python/password_cracking_sequential.py +++ b/vs/python/password_cracking_sequential.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3.9 - """Program for cracking the password consisting with only numbers using brute force approach sequentially"""