|
| 1 | +""" |
| 2 | +P2P File Sharing Implementation |
| 3 | +Author: Nikhil Karoriya |
| 4 | +Description: This module implements a secure peer-to-peer file sharing algorithm |
| 5 | +using socket programming, AES for data encryption, RSA for key exchange and Zlib for data compression |
| 6 | +
|
| 7 | +Usage: |
| 8 | + python peer.py --listen-port 5001 |
| 9 | +
|
| 10 | +You will be prompted for: |
| 11 | +
|
| 12 | + Send file (y/n)? y |
| 13 | + Enter file path to send: sample.txt |
| 14 | + Enter receiver's IP address: localhost (or receiver's IP address) |
| 15 | + Enter receiver's port: 6001 |
| 16 | +""" |
| 17 | + |
| 18 | + |
| 19 | +import os |
| 20 | +import socket |
| 21 | +import threading |
| 22 | +import traceback |
| 23 | +from tqdm import tqdm |
| 24 | +import argparse |
| 25 | +from time import sleep |
| 26 | +from crypto.rsa_crypto import ( |
| 27 | + decrypt_with_rsa_private_key, |
| 28 | + encrypt_with_rsa_public_key, |
| 29 | + generate_rsa_key_pair, |
| 30 | + is_valid_pem_key |
| 31 | +) |
| 32 | +from crypto.aes_crypto import generate_aes_key, encrypt_chunk_with_aes, decrypt_chunk_with_aes |
| 33 | +from utils.file_utils import ensure_dir, sha256_digest_stream, is_valid_ip, is_valid_port |
| 34 | +from utils.file_utils import CHUNK_SIZE |
| 35 | + |
| 36 | +PRIVATE_KEY_PATH = 'keys/private_key.pem' |
| 37 | +PUBLIC_KEY_PATH = 'keys/public_keys.pem' |
| 38 | +RECEIVE_DIR = 'received_files' |
| 39 | + |
| 40 | +ensure_dir(RECEIVE_DIR) |
| 41 | +ensure_dir("keys") |
| 42 | + |
| 43 | +if not is_valid_pem_key(PRIVATE_KEY_PATH, is_private=True) or not is_valid_pem_key(PUBLIC_KEY_PATH, is_private=False): |
| 44 | + print("\n[!] RSA key missing or invalid. Regenerating...") |
| 45 | + generate_rsa_key_pair(PRIVATE_KEY_PATH, PUBLIC_KEY_PATH) |
| 46 | + print("\n[+] RSA key pair regenerated.") |
| 47 | + |
| 48 | +parser = argparse.ArgumentParser() |
| 49 | +parser.add_argument("--listen-port", type=int, required=True) |
| 50 | +args = parser.parse_args() |
| 51 | + |
| 52 | +LISTEN_PORT = args.listen_port |
| 53 | + |
| 54 | +def peer_listener(): |
| 55 | + try: |
| 56 | + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 57 | + server_socket.bind(('0.0.0.0', LISTEN_PORT)) |
| 58 | + server_socket.listen(1) |
| 59 | + print(f"\n[+] Listening on port {LISTEN_PORT}...") |
| 60 | + |
| 61 | + while True: |
| 62 | + client_socket, addr = server_socket.accept() |
| 63 | + print(f"\n\n[+] Incoming connection from {addr}\n") |
| 64 | + |
| 65 | + try: |
| 66 | + key_size = int.from_bytes(client_socket.recv(4), 'big') |
| 67 | + encrypted_key = client_socket.recv(key_size) |
| 68 | + |
| 69 | + name_len = int.from_bytes(client_socket.recv(4), 'big') |
| 70 | + file_name = client_socket.recv(name_len).decode() |
| 71 | + |
| 72 | + extension = int.from_bytes(client_socket.recv(4), 'big') |
| 73 | + file_extension = client_socket.recv(extension).decode() |
| 74 | + |
| 75 | + total_size = int.from_bytes(client_socket.recv(8), 'big') |
| 76 | + |
| 77 | + aes_key = decrypt_with_rsa_private_key(encrypted_key, PRIVATE_KEY_PATH) |
| 78 | + |
| 79 | + file_path = os.path.join(RECEIVE_DIR, file_name) |
| 80 | + |
| 81 | + with open(file_path, 'wb') as f_out, tqdm(total=total_size, desc=f"[+] Receiving {file_name}", unit="B", unit_scale=True) as pbar: |
| 82 | + received_bytes = 0 |
| 83 | + while received_bytes < total_size: |
| 84 | + rcv_chunk_size = int.from_bytes(client_socket.recv(4), 'big') |
| 85 | + encrypted_chunk = b"" |
| 86 | + |
| 87 | + while len(encrypted_chunk) < rcv_chunk_size: |
| 88 | + part = client_socket.recv(rcv_chunk_size - len(encrypted_chunk)) |
| 89 | + if not part: |
| 90 | + raise Exception("\n[!] Connection lost during transfer.") |
| 91 | + encrypted_chunk += part |
| 92 | + |
| 93 | + decrypted_chunk = decrypt_chunk_with_aes(encrypted_chunk, aes_key, file_extension) |
| 94 | + f_out.write(decrypted_chunk) |
| 95 | + received_bytes += len(decrypted_chunk) |
| 96 | + pbar.update(len(decrypted_chunk)) |
| 97 | + |
| 98 | + print(f"\n[+] File saved to {file_path}") |
| 99 | + file_hash = sha256_digest_stream(file_path) |
| 100 | + print(f"\n[+] SHA256 Hash: {file_hash}") |
| 101 | + |
| 102 | + except Exception as e: |
| 103 | + print(f"\n[!] ERROR receiving file: {str(e)}\n{traceback.format_exc()}") |
| 104 | + finally: |
| 105 | + client_socket.close() |
| 106 | + |
| 107 | + except Exception as e: |
| 108 | + print(f"\n[!] Server failed: {str(e)}\n{traceback.format_exc()}") |
| 109 | + |
| 110 | +def send_file(file_path, peer_ip, peer_port): |
| 111 | + try: |
| 112 | + |
| 113 | + file_name = os.path.basename(file_path) |
| 114 | + file_extension = os.path.splitext(file_name)[1].lower() |
| 115 | + |
| 116 | + aes_key = generate_aes_key() |
| 117 | + encrypted_key = encrypt_with_rsa_public_key(aes_key, PUBLIC_KEY_PATH) |
| 118 | + file_size = os.path.getsize(file_path) |
| 119 | + |
| 120 | + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 121 | + client_socket.connect((peer_ip, peer_port)) |
| 122 | + |
| 123 | + client_socket.sendall(len(encrypted_key).to_bytes(4, 'big')) |
| 124 | + client_socket.sendall(encrypted_key) |
| 125 | + |
| 126 | + client_socket.sendall(len(file_name.encode()).to_bytes(4, 'big')) |
| 127 | + client_socket.sendall(file_name.encode()) |
| 128 | + |
| 129 | + client_socket.sendall(len(file_extension.encode()).to_bytes(4, 'big')) |
| 130 | + client_socket.sendall(file_extension.encode()) |
| 131 | + |
| 132 | + client_socket.sendall(file_size.to_bytes(8, 'big')) |
| 133 | + |
| 134 | + print() |
| 135 | + |
| 136 | + with open(file_path, 'rb') as f, tqdm(total=file_size, desc=f"[+] Sending {file_name}", unit="B", unit_scale=True) as pbar: |
| 137 | + for chunk in iter(lambda: f.read(CHUNK_SIZE), b''): |
| 138 | + encrypted_chunk = encrypt_chunk_with_aes(chunk, aes_key, file_extension) |
| 139 | + client_socket.sendall(len(encrypted_chunk).to_bytes(4, 'big')) |
| 140 | + client_socket.sendall(encrypted_chunk) |
| 141 | + pbar.update(len(chunk)) |
| 142 | + |
| 143 | + print(f"\n[+] File '{file_name}' sent successfully to {peer_ip}:{peer_port}.") |
| 144 | + client_socket.close() |
| 145 | + |
| 146 | + except Exception as e: |
| 147 | + print(f"\n[!] Failed to send file: {str(e)}\n{traceback.format_exc()}") |
| 148 | + |
| 149 | + |
| 150 | +if __name__ == '__main__': |
| 151 | + |
| 152 | + try: |
| 153 | + threading.Thread(target=peer_listener, daemon=True).start() |
| 154 | + sleep(0.5) |
| 155 | + |
| 156 | + while True: |
| 157 | + user_input = input("\nSend file [y], wait [w], or exit [e]? ").strip().lower() |
| 158 | + |
| 159 | + if user_input == 'e': |
| 160 | + print("\n[INFO] Exiting...") |
| 161 | + break |
| 162 | + |
| 163 | + elif user_input == 'y': |
| 164 | + file_path = input("\nEnter file path to send: ").strip() |
| 165 | + peer_ip = input("\nEnter receiver's IP address: ").strip() |
| 166 | + peer_port_input = input("\nEnter receiver's port: ").strip() |
| 167 | + |
| 168 | + if not is_valid_port(peer_port_input) or not is_valid_ip(peer_ip): |
| 169 | + print("\n[!] Receiver IP or port not correct/specified.") |
| 170 | + continue |
| 171 | + |
| 172 | + if not os.path.isfile(file_path): |
| 173 | + print("\n[!] File does not exist.") |
| 174 | + continue |
| 175 | + |
| 176 | + peer_port = int(peer_port_input) |
| 177 | + send_file(file_path, peer_ip, peer_port) |
| 178 | + |
| 179 | + elif user_input == 'w': |
| 180 | + print("\n[INFO] Waiting for incoming transfers...") |
| 181 | + |
| 182 | + else: |
| 183 | + print("\n[!] Invalid input. Use 'y', 'w', or 'e'.") |
| 184 | + |
| 185 | + except KeyboardInterrupt: |
| 186 | + print("\n[INFO] Exiting by keyboard interrupt.") |
| 187 | + |
| 188 | + except Exception as e: |
| 189 | + print(f"\n[!] An error occurred: {str(e)}\n{traceback.format_exc()}") |
| 190 | + |
| 191 | + |
0 commit comments