Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
  • Loading branch information
pre-commit-ci[bot] committed Oct 10, 2025
commit 7cdd1c3faded894ea401667cad2e5fbede96ee21
28 changes: 22 additions & 6 deletions peer2peer_file_sharing/crypto/aes_crypto.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
import zlib

Check failure on line 1 in peer2peer_file_sharing/crypto/aes_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (INP001)

peer2peer_file_sharing/crypto/aes_crypto.py:1:1: INP001 File `peer2peer_file_sharing/crypto/aes_crypto.py` is part of an implicit namespace package. Add an `__init__.py`.
from cryptography.fernet import Fernet

Check failure on line 2 in peer2peer_file_sharing/crypto/aes_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

peer2peer_file_sharing/crypto/aes_crypto.py:1:1: I001 Import block is un-sorted or un-formatted

SKIP_COMPRESSION_EXTENSIONS = {
'.zip', '.gz', '.bz2', '.xz', '.rar', '.7z',
'.jpg', '.jpeg', '.png', '.gif', '.webp',
'.mp3', '.mp4', '.avi', '.mkv', '.mov',
'.pdf'
".zip",
".gz",
".bz2",
".xz",
".rar",
".7z",
".jpg",
".jpeg",
".png",
".gif",
".webp",
".mp3",
".mp4",
".avi",
".mkv",
".mov",
".pdf",
}


def generate_aes_key():
return Fernet.generate_key()


def encrypt_chunk_with_aes(chunk: bytes, aes_key: bytes, file_extension: str) -> bytes:
fernet = Fernet(aes_key)
if file_extension.lower() not in SKIP_COMPRESSION_EXTENSIONS:
try:
chunk = zlib.compress(chunk)
except Exception as e:

Check failure on line 34 in peer2peer_file_sharing/crypto/aes_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (BLE001)

peer2peer_file_sharing/crypto/aes_crypto.py:34:16: BLE001 Do not catch blind exception: `Exception`
print(f"\n[!] Compression failed: {e}")

return fernet.encrypt(chunk)


def decrypt_chunk_with_aes(chunk: bytes, aes_key: bytes, file_extension: str) -> bytes:
fernet = Fernet(aes_key)
try:
decrypted_data = fernet.decrypt(chunk)
except Exception as e:

Check failure on line 44 in peer2peer_file_sharing/crypto/aes_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (BLE001)

peer2peer_file_sharing/crypto/aes_crypto.py:44:12: BLE001 Do not catch blind exception: `Exception`
raise Exception(f"\n[!] AES decryption failed: {e}")

Check failure on line 45 in peer2peer_file_sharing/crypto/aes_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (EM102)

peer2peer_file_sharing/crypto/aes_crypto.py:45:25: EM102 Exception must not use an f-string literal, assign to variable first

if file_extension.lower() not in SKIP_COMPRESSION_EXTENSIONS:
try:
Expand All @@ -34,4 +50,4 @@
except zlib.error:
print("\n[!] Warning: Failed to decompress — file may not be compressed")

return decrypted_data
return decrypted_data
54 changes: 32 additions & 22 deletions peer2peer_file_sharing/crypto/rsa_crypto.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,63 @@
from cryptography.hazmat.primitives import serialization, hashes

Check failure on line 1 in peer2peer_file_sharing/crypto/rsa_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (INP001)

peer2peer_file_sharing/crypto/rsa_crypto.py:1:1: INP001 File `peer2peer_file_sharing/crypto/rsa_crypto.py` is part of an implicit namespace package. Add an `__init__.py`.
from cryptography.hazmat.primitives.asymmetric import rsa, padding

Check failure on line 2 in peer2peer_file_sharing/crypto/rsa_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

peer2peer_file_sharing/crypto/rsa_crypto.py:1:1: I001 Import block is un-sorted or un-formatted


def generate_rsa_key_pair(private_path: str, public_path: str):
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)

with open(private_path, "wb") as f:

f.write(private_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()
))

f.write(
private_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption(),
)
)

with open(public_path, "wb") as f:

f.write(private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo
))
f.write(
private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo,
)
)

def encrypt_with_rsa_public_key(data: bytes, public_key_path: str) -> bytes:

def encrypt_with_rsa_public_key(data: bytes, public_key_path: str) -> bytes:
with open(public_key_path, "rb") as f:
public_key = serialization.load_pem_public_key(f.read())

if not isinstance(public_key, rsa.RSAPublicKey):
raise TypeError("\n[!] The loaded public key is not an RSA key.")

return public_key.encrypt(
data,
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)


def decrypt_with_rsa_private_key(ciphertext: bytes, private_key_path: str) -> bytes:

with open(private_key_path, "rb") as f:
private_key = serialization.load_pem_private_key(f.read(), password=None)

if not isinstance(private_key, rsa.RSAPrivateKey):
raise TypeError("\n[!] The loaded private key is not an RSA key.")

return private_key.decrypt(
ciphertext,
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
),
)


def is_valid_pem_key(file_path: str, is_private: bool = False) -> bool:

try:
with open(file_path, "rb") as f:
data = f.read()
Expand All @@ -57,7 +68,6 @@
else:
serialization.load_pem_public_key(data)
return True

except Exception:

Check failure on line 72 in peer2peer_file_sharing/crypto/rsa_crypto.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (BLE001)

peer2peer_file_sharing/crypto/rsa_crypto.py:72:12: BLE001 Do not catch blind exception: `Exception`
return False

110 changes: 71 additions & 39 deletions peer2peer_file_sharing/peer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""

Check failure on line 1 in peer2peer_file_sharing/peer.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (INP001)

peer2peer_file_sharing/peer.py:1:1: INP001 File `peer2peer_file_sharing/peer.py` is part of an implicit namespace package. Add an `__init__.py`.
P2P File Sharing Implementation
Author: Nikhil Karoriya
Description: This module implements a secure peer-to-peer file sharing algorithm
using socket programming, AES for data encryption, RSA for key exchange and Zlib for data compression

Check failure on line 5 in peer2peer_file_sharing/peer.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E501)

peer2peer_file_sharing/peer.py:5:89: E501 Line too long (101 > 88)

Usage:
python peer.py --listen-port 5001
Expand All @@ -15,7 +15,6 @@
Enter receiver's port: 6001
"""


import os
import socket
import threading
Expand All @@ -27,20 +26,31 @@
decrypt_with_rsa_private_key,
encrypt_with_rsa_public_key,
generate_rsa_key_pair,
is_valid_pem_key
is_valid_pem_key,
)
from crypto.aes_crypto import (
generate_aes_key,
encrypt_chunk_with_aes,
decrypt_chunk_with_aes,
)
from utils.file_utils import (
ensure_dir,
sha256_digest_stream,
is_valid_ip,
is_valid_port,
)
from crypto.aes_crypto import generate_aes_key, encrypt_chunk_with_aes, decrypt_chunk_with_aes
from utils.file_utils import ensure_dir, sha256_digest_stream, is_valid_ip, is_valid_port
from utils.file_utils import CHUNK_SIZE

PRIVATE_KEY_PATH = 'keys/private_key.pem'
PUBLIC_KEY_PATH = 'keys/public_keys.pem'
RECEIVE_DIR = 'received_files'
PRIVATE_KEY_PATH = "keys/private_key.pem"
PUBLIC_KEY_PATH = "keys/public_keys.pem"
RECEIVE_DIR = "received_files"

ensure_dir(RECEIVE_DIR)
ensure_dir("keys")

if not is_valid_pem_key(PRIVATE_KEY_PATH, is_private=True) or not is_valid_pem_key(PUBLIC_KEY_PATH, is_private=False):
if not is_valid_pem_key(PRIVATE_KEY_PATH, is_private=True) or not is_valid_pem_key(
PUBLIC_KEY_PATH, is_private=False
):
print("\n[!] RSA key missing or invalid. Regenerating...")
generate_rsa_key_pair(PRIVATE_KEY_PATH, PUBLIC_KEY_PATH)
print("\n[+] RSA key pair regenerated.")
Expand All @@ -51,10 +61,11 @@

LISTEN_PORT = args.listen_port


def peer_listener():
try:
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', LISTEN_PORT))
server_socket.bind(("0.0.0.0", LISTEN_PORT))
server_socket.listen(1)
print(f"\n[+] Listening on port {LISTEN_PORT}...")

Expand All @@ -63,34 +74,48 @@
print(f"\n\n[+] Incoming connection from {addr}\n")

try:
key_size = int.from_bytes(client_socket.recv(4), 'big')
key_size = int.from_bytes(client_socket.recv(4), "big")
encrypted_key = client_socket.recv(key_size)

name_len = int.from_bytes(client_socket.recv(4), 'big')
name_len = int.from_bytes(client_socket.recv(4), "big")
file_name = client_socket.recv(name_len).decode()

extension = int.from_bytes(client_socket.recv(4), 'big')
extension = int.from_bytes(client_socket.recv(4), "big")
file_extension = client_socket.recv(extension).decode()

total_size = int.from_bytes(client_socket.recv(8), 'big')
total_size = int.from_bytes(client_socket.recv(8), "big")

aes_key = decrypt_with_rsa_private_key(encrypted_key, PRIVATE_KEY_PATH)

file_path = os.path.join(RECEIVE_DIR, file_name)

with open(file_path, 'wb') as f_out, tqdm(total=total_size, desc=f"[+] Receiving {file_name}", unit="B", unit_scale=True) as pbar:

with (
open(file_path, "wb") as f_out,
tqdm(
total=total_size,
desc=f"[+] Receiving {file_name}",
unit="B",
unit_scale=True,
) as pbar,
):
received_bytes = 0
while received_bytes < total_size:
rcv_chunk_size = int.from_bytes(client_socket.recv(4), 'big')
rcv_chunk_size = int.from_bytes(client_socket.recv(4), "big")
encrypted_chunk = b""

while len(encrypted_chunk) < rcv_chunk_size:
part = client_socket.recv(rcv_chunk_size - len(encrypted_chunk))
part = client_socket.recv(
rcv_chunk_size - len(encrypted_chunk)
)
if not part:
raise Exception("\n[!] Connection lost during transfer.")
raise Exception(
"\n[!] Connection lost during transfer."
)
encrypted_chunk += part

decrypted_chunk = decrypt_chunk_with_aes(encrypted_chunk, aes_key, file_extension)
decrypted_chunk = decrypt_chunk_with_aes(
encrypted_chunk, aes_key, file_extension
)
f_out.write(decrypted_chunk)
received_bytes += len(decrypted_chunk)
pbar.update(len(decrypted_chunk))
Expand All @@ -107,9 +132,9 @@
except Exception as e:
print(f"\n[!] Server failed: {str(e)}\n{traceback.format_exc()}")


def send_file(file_path, peer_ip, peer_port):
try:

file_name = os.path.basename(file_path)
file_extension = os.path.splitext(file_name)[1].lower()

Expand All @@ -120,23 +145,31 @@
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((peer_ip, peer_port))

client_socket.sendall(len(encrypted_key).to_bytes(4, 'big'))
client_socket.sendall(len(encrypted_key).to_bytes(4, "big"))
client_socket.sendall(encrypted_key)

client_socket.sendall(len(file_name.encode()).to_bytes(4, 'big'))
client_socket.sendall(len(file_name.encode()).to_bytes(4, "big"))
client_socket.sendall(file_name.encode())

client_socket.sendall(len(file_extension.encode()).to_bytes(4, 'big'))
client_socket.sendall(len(file_extension.encode()).to_bytes(4, "big"))
client_socket.sendall(file_extension.encode())

client_socket.sendall(file_size.to_bytes(8, 'big'))
client_socket.sendall(file_size.to_bytes(8, "big"))

print()

with open(file_path, 'rb') as f, tqdm(total=file_size, desc=f"[+] Sending {file_name}", unit="B", unit_scale=True) as pbar:
for chunk in iter(lambda: f.read(CHUNK_SIZE), b''):
with (
open(file_path, "rb") as f,
tqdm(
total=file_size,
desc=f"[+] Sending {file_name}",
unit="B",
unit_scale=True,
) as pbar,
):
for chunk in iter(lambda: f.read(CHUNK_SIZE), b""):
encrypted_chunk = encrypt_chunk_with_aes(chunk, aes_key, file_extension)
client_socket.sendall(len(encrypted_chunk).to_bytes(4, 'big'))
client_socket.sendall(len(encrypted_chunk).to_bytes(4, "big"))
client_socket.sendall(encrypted_chunk)
pbar.update(len(chunk))

Expand All @@ -147,20 +180,21 @@
print(f"\n[!] Failed to send file: {str(e)}\n{traceback.format_exc()}")


if __name__ == '__main__':

if __name__ == "__main__":
try:
threading.Thread(target=peer_listener, daemon=True).start()
sleep(0.5)

while True:
user_input = input("\nSend file [y], wait [w], or exit [e]? ").strip().lower()
user_input = (
input("\nSend file [y], wait [w], or exit [e]? ").strip().lower()
)

if user_input == 'e':
if user_input == "e":
print("\n[INFO] Exiting...")
break

elif user_input == 'y':
elif user_input == "y":
file_path = input("\nEnter file path to send: ").strip()
peer_ip = input("\nEnter receiver's IP address: ").strip()
peer_port_input = input("\nEnter receiver's port: ").strip()
Expand All @@ -176,16 +210,14 @@
peer_port = int(peer_port_input)
send_file(file_path, peer_ip, peer_port)

elif user_input == 'w':
elif user_input == "w":
print("\n[INFO] Waiting for incoming transfers...")

else:
print("\n[!] Invalid input. Use 'y', 'w', or 'e'.")

except KeyboardInterrupt:
print("\n[INFO] Exiting by keyboard interrupt.")

except Exception as e:
print(f"\n[!] An error occurred: {str(e)}\n{traceback.format_exc()}")


Loading
Loading