Skip to content

Commit

Permalink
password_cracking_parallel
Browse files Browse the repository at this point in the history
  • Loading branch information
s50600822 committed Mar 17, 2024
1 parent caafc3c commit 7e25f8e
Show file tree
Hide file tree
Showing 9 changed files with 458 additions and 2 deletions.
35 changes: 35 additions & 0 deletions vs/python/password_cracking_parallel/README.md
Original file line number Diff line number Diff line change
@@ -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
```
33 changes: 33 additions & 0 deletions vs/python/password_cracking_parallel/library_thread_pool.py
Original file line number Diff line number Diff line change
@@ -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()
42 changes: 42 additions & 0 deletions vs/python/password_cracking_parallel/message_queue.py
Original file line number Diff line number Diff line change
@@ -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)
93 changes: 93 additions & 0 deletions vs/python/password_cracking_parallel/password_cracking_parallel.py
Original file line number Diff line number Diff line change
@@ -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)
53 changes: 53 additions & 0 deletions vs/python/password_cracking_parallel/pipe.py
Original file line number Diff line number Diff line change
@@ -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()
54 changes: 54 additions & 0 deletions vs/python/password_cracking_parallel/shared_ipc.py
Original file line number Diff line number Diff line change
@@ -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()
75 changes: 75 additions & 0 deletions vs/python/password_cracking_parallel/sockets.py
Original file line number Diff line number Diff line change
@@ -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()
Loading

0 comments on commit 7e25f8e

Please sign in to comment.