From fefc4dda8dec533bb911d4a9111460447c6b2056 Mon Sep 17 00:00:00 2001 From: ShreyashkumarDube <86827868+ShreyashkumarDube@users.noreply.github.com> Date: Fri, 3 Oct 2025 07:01:58 +0530 Subject: [PATCH 1/2] Added passkey-based file encryption, multiple file upload/download support, updated Main and FileTesting --- src/Main/Main.java | 236 +++++++-------------- src/discovery/FileData.java | 55 ++--- src/discovery/Handshake.java | 203 ++++++++---------- src/downloads/samp.txt | 1 + src/downloads/sample.txt | 0 src/downloads/sample2.txt | 0 src/p2p/ConnectionHandlerSequential.java | 259 +++++++++++------------ src/p2p/FileReciever.java | 146 ++++++------- src/testing/FileTesting.java | 158 +++++++------- src/utils/CryptoUtils.java | 47 ++++ 10 files changed, 507 insertions(+), 598 deletions(-) create mode 100644 src/downloads/samp.txt create mode 100644 src/downloads/sample.txt create mode 100644 src/downloads/sample2.txt create mode 100644 src/utils/CryptoUtils.java diff --git a/src/Main/Main.java b/src/Main/Main.java index 0c5e865..1598c5f 100644 --- a/src/Main/Main.java +++ b/src/Main/Main.java @@ -1,186 +1,94 @@ package Main; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; import java.util.Scanner; -import discovery.CentralRegistry; import discovery.FileData; import discovery.Handshake; import discovery.Node; -import discovery.messages.CentralRegistryRequest; -import discovery.messages.CentralRegistryResponse; -import discovery.messages.FileRequest; -import discovery.messages.FileResponse; -import discovery.messages.TransferRequest; -import discovery.messages.TransferResponse; -import p2p.BroadCastTransfer; -import p2p.ConnectionHandlerSequential; +import discovery.CentralRegistry; import p2p.FileReciever; -import p2p.ObjectTransfer; -import testing.FileTesting; -import utils.Config; public class Main { - public static void main(String[] args) throws IOException { - // Initialize nodes - - // args are as follows - // args[0] = central / peer -> decides the role - // args[1] = IP of self ( for listening for connections) - // args[2] = port of self - // args[3] = only if it is peer // (central IP) - // args[4] = only if it is a peer (central port) - - - - if(args[0].equals("testing")) { - String filePath = new File(Config.getTestDir(), args[1]).getPath(); - int maxLines = 1000; // Assume a maximum number of lines in the file - String[] paths = new String[maxLines]; // Create an array with the assumed size - int index = 0; - - try (BufferedReader br = new BufferedReader(new FileReader(filePath))) { - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); // Trim the line to remove any leading/trailing whitespace - if (!line.isEmpty()) { // Ignore empty lines - if (index >= maxLines) { - throw new RuntimeException("Exceeded maximum number of lines: " + maxLines); - } - paths[index++] = line; // Add the path to the array - } - } - } catch (IOException e) { - throw new RuntimeException("Failed to read the file: " + filePath, e); - } - - // Resize the array to the actual number of paths read - String[] result = new String[index]; - System.arraycopy(paths, 0, result, 0, index); - FileTesting.test(result); - } - - - if(args[0].equals("central")) { - Node central = new Node(); - central.setPeerIP(args[1]); - central.setPeerPort(Integer.parseInt(args[2])); - CentralRegistry.start(central); + public static void main(String[] args) throws IOException { + if (args.length < 1) { + System.out.println("Usage: java Main [args...]"); + return; } - - - if(args[0].equals("peer")) { - // one does this - Node client = new Node(); - client.setPeerIP(args[1]); - client.setPeerPort(Integer.parseInt(args[2])); - - - - - - Node central = new Node(); - central.setPeerIP(args[3]); - central.setPeerPort(Integer.parseInt(args[4])); - - - - - - - try { - - - - Handshake.setClient(client); - Handshake.setCentralRegistry(central); - - - - - Thread t = new Thread(() -> Handshake.start(client.getPeerPort() , client)); - t.start(); //this thread line will be started for each of the peer - - - Scanner scanner = new Scanner(System.in); - System.out.print("Enter command (upload or download ): "); - - while (true) { - - String userInput = scanner.nextLine().trim(); - - // Exit condition - if (userInput.equalsIgnoreCase("exit")) { - System.out.println("Exiting program..."); - break; - } - // Split the input into command and argument - String[] parts = userInput.split(" "); - if (parts.length < 2) { - System.out.println("Invalid command. Usage: upload or download "); - continue; - } + if (args[0].equals("central")) { + Node central = new Node(); + central.setPeerIP(args[1]); + central.setPeerPort(Integer.parseInt(args[2])); + System.out.println("Central Registry started..."); + CentralRegistry.start(central); // blocks and handles requests + } + + else if (args[0].equals("peer")) { + Node client = new Node(); + client.setPeerIP(args[1]); + client.setPeerPort(Integer.parseInt(args[2])); + + Node central = new Node(); + central.setPeerIP(args[3]); + central.setPeerPort(Integer.parseInt(args[4])); + + Handshake.setClient(client); + Handshake.setCentralRegistry(central); + + Thread handshakeThread = new Thread(() -> { + Handshake.start(client.getPeerPort(), client); + }); + handshakeThread.start(); - String command = parts[0]; - String argument = parts[1]; - - // Process the command - switch (command.toLowerCase()) { - case "upload": - // Start a new thread for upload - FileData fileupload = new FileData(argument); - System.out.println("The Hash of the File is " + fileupload.getFileHash()); - Handshake.registerFile(fileupload , argument); - break; - case "download": - // Start a new thread for download - FileReciever.downloadFile(argument, central); - - break; - case "broadcastfile": - FileData f = new FileData(argument); - - System.out.println("The File Hash of the File to be broadcasted is " + f.getFileHash()); - System.out.println("Type broadcast to enter broadcasting period"); - - userInput = scanner.nextLine().trim(); - BroadCastTransfer.BroadcastFile(f , Handshake.getClient() , argument); - break; - - case "broadcastrecieve": - BroadCastTransfer.RecieveFile(argument); - break; - - default: - System.out.println("Invalid command. Usage: upload or download "); - break; + System.out.println("Peer started. Commands: upload , download , exit"); + + Scanner sc = new Scanner(System.in); + + while (true) { + System.out.print("> "); + String command = sc.nextLine(); + + if (command.startsWith("upload ")) { + String pathsStr = command.substring(7).trim(); + String[] files = pathsStr.split(";"); + System.out.print("Enter passkey for these file(s): "); + String passkey = sc.nextLine(); + + for (String filePath : files) { + File fCheck = new File(filePath.trim()); + if (!fCheck.exists()) { + System.out.println("File not found: " + filePath); + continue; + } + try { + FileData f = new FileData(filePath.trim()); + Handshake.registerFile(f, filePath.trim(), passkey); + System.out.println("File registered: " + filePath); + System.out.println("File hash: " + f.getFileHash()); // <-- HASH PRINTED HERE + } catch (IOException e) { + System.out.println("Error reading file: " + filePath); + } } } - scanner.close(); - + else if (command.startsWith("download ")) { + String fileHash = command.substring(9).trim(); + System.out.print("Enter passkey to download the file: "); + String passkey = sc.nextLine(); + FileReciever.downloadFile(fileHash, central, passkey); + } - - } catch (IOException e) { - e.printStackTrace(); + else if (command.equals("exit")) { + System.out.println("Exiting..."); + break; + } + + else { + System.out.println("Invalid command."); + } } } - - -// System.out.println("GO CHECK README FOR TUTORIAL"); - - - } -} \ No newline at end of file +} diff --git a/src/discovery/FileData.java b/src/discovery/FileData.java index 2e81549..50c74d4 100644 --- a/src/discovery/FileData.java +++ b/src/discovery/FileData.java @@ -8,7 +8,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -public class FileData implements Serializable{ +public class FileData implements Serializable { // Fields representing file metadata private String fileName; @@ -16,6 +16,10 @@ public class FileData implements Serializable{ private String fileType; // MIME type or file extension private String fileHash; // Checksum or hash for integrity verification + // New fields for passkey-protected encryption + private String encFileKeyBase64; // Encrypted AES file key, Base64 encoded + private String saltBase64; // Salt for PBKDF2 key derivation, Base64 encoded + // Constructors public FileData() { // Default constructor @@ -44,45 +48,30 @@ public FileData(String filePath) throws IOException { this.fileHash = calculateFileHash(file); } - // Getters and Setters - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - - public long getFileSize() { - return fileSize; - } + // Getters and Setters for existing fields + public String getFileName() { return fileName; } + public void setFileName(String fileName) { this.fileName = fileName; } - public void setFileSize(long fileSize) { - this.fileSize = fileSize; - } + public long getFileSize() { return fileSize; } + public void setFileSize(long fileSize) { this.fileSize = fileSize; } - public String getFileType() { - return fileType; - } + public String getFileType() { return fileType; } + public void setFileType(String fileType) { this.fileType = fileType; } - public void setFileType(String fileType) { - this.fileType = fileType; - } + public String getFileHash() { return fileHash; } + public void setFileHash(String fileHash) { this.fileHash = fileHash; } - public String getFileHash() { - return fileHash; - } + // Getters and Setters for new fields + public String getEncFileKeyBase64() { return encFileKeyBase64; } + public void setEncFileKeyBase64(String encFileKeyBase64) { this.encFileKeyBase64 = encFileKeyBase64; } - public void setFileHash(String fileHash) { - this.fileHash = fileHash; - } + public String getSaltBase64() { return saltBase64; } + public void setSaltBase64(String saltBase64) { this.saltBase64 = saltBase64; } // Helper method to extract the file extension private String getFileExtension(String fileName) { int lastDotIndex = fileName.lastIndexOf('.'); - if (lastDotIndex == -1) { - return ""; // No extension - } + if (lastDotIndex == -1) return ""; // No extension return fileName.substring(lastDotIndex + 1); } @@ -116,6 +105,8 @@ public String toString() { ", fileSize=" + fileSize + ", fileType='" + fileType + '\'' + ", fileHash='" + fileHash + '\'' + + ", encFileKeyBase64='" + encFileKeyBase64 + '\'' + + ", saltBase64='" + saltBase64 + '\'' + '}'; } -} \ No newline at end of file +} diff --git a/src/discovery/Handshake.java b/src/discovery/Handshake.java index 9f1d0af..cf50ecd 100644 --- a/src/discovery/Handshake.java +++ b/src/discovery/Handshake.java @@ -1,125 +1,110 @@ package discovery; -import discovery.messages.CentralRegistryRequest; -import discovery.messages.CentralRegistryResponse; -import discovery.messages.FileRequest; -import discovery.messages.FileResponse; -import discovery.messages.TransferRequest; -import discovery.messages.TransferResponse; -import java.io.*; -import java.net.*; + +import discovery.messages.*; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; import java.util.HashMap; import java.util.Map; + import p2p.ConnectionHandlerSequential; import p2p.ObjectTransfer; + public class Handshake { - // this class has everything required for the uploader of the file - - private static Node client; // client peer registered + private static Node client; private static Node CentralRegistry; - - - private static Map HashtoFD = new HashMap<>(); - private static Map HashtoPath = new HashMap<>(); - - public static void start(int port , Node c) { - -// registerToCentralRegistry(port); - System.out.println("handshake on"); - System.out.print(port); - - client = c; - while(true) { - try (ServerSocket serverSocket = new ServerSocket(client.getPeerPort())) { - + // Maps file hash -> FileData, file hash -> path, file hash -> passkey + private static Map HashtoFD = new HashMap<>(); + private static Map HashtoPath = new HashMap<>(); + private static Map HashtoPasskey = new HashMap<>(); + + // Start the peer server to listen for file requests + public static void start(int port, Node c) { + client = c; + System.out.println("handshake on " + port); + + while (true) { + try (ServerSocket serverSocket = new ServerSocket(client.getPeerPort())) { Socket socket = serverSocket.accept(); - Object obj = ObjectTransfer.receiveObject(socket); - System.out.println("FileRequst Recieved"); - FileRequest req = (FileRequest)obj; - FileResponse res; - - if(HashtoFD.containsKey(req.FileData.getFileHash())) { - res = new FileResponse(req.RequestingNode,client,true,Handshake.HashtoFD.get(req.FileData.getFileHash())); - System.out.println("FileResponse Sentding"); - ObjectTransfer.sendObject(socket, res); - System.out.println("FileResponse Sent"); - obj = ObjectTransfer.receiveObject(socket); - - - System.out.println("TransferRequest Recieved"); - TransferRequest treq = (TransferRequest)obj; - System.out.println("TransferResponse Senting"); - TransferResponse tres = new TransferResponse(treq.RequestingNode , client , treq.Port , true); - System.out.println("TransferResponse Sent"); - ObjectTransfer.sendObject(socket, tres); - - - socket.close(); - - System.out.println("the public key of requesting node is " + req.pub ); - ConnectionHandlerSequential.sendFile(tres.RequestingNode.getPeerIP() , tres.Port , Handshake.HashtoPath.get(treq.Fd.getFileHash()) , req.pub); - - + Object obj = ObjectTransfer.receiveObject(socket); + + if (obj instanceof FileRequest) { + FileRequest req = (FileRequest) obj; + + if (HashtoFD.containsKey(req.FileData.getFileHash())) { + + // Send FileResponse + FileResponse res = new FileResponse( + req.RequestingNode, + client, + true, + HashtoFD.get(req.FileData.getFileHash()) + ); + ObjectTransfer.sendObject(socket, res); + + obj = ObjectTransfer.receiveObject(socket); + if (obj instanceof TransferRequest) { + TransferRequest treq = (TransferRequest) obj; + TransferResponse tres = new TransferResponse( + treq.RequestingNode, + client, + treq.Port, + true + ); + ObjectTransfer.sendObject(socket, tres); + + // Automatic file transfer with passkey + String passkeyToUse = HashtoPasskey.getOrDefault( + treq.Fd.getFileHash(), + "default123" + ); + + ConnectionHandlerSequential.sendFile( + tres.RequestingNode.getPeerIP(), + tres.Port, + HashtoPath.get(treq.Fd.getFileHash()), + req.pub, + passkeyToUse + ); + } + } } - - + + socket.close(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); - } - } - + } } - - - - - public static void registerFile(FileData f , String path) { - // send fileData to server - Node central = Handshake.getCentralRegistry(); - Node c = Handshake.getClient(); - CentralRegistryRequest req = new CentralRegistryRequest(f , c); - - - try { - Socket socket = new Socket(central.getPeerIP() , central.getPeerPort()); - ObjectTransfer.sendObject(socket, req); - - CentralRegistryResponse res = (CentralRegistryResponse)ObjectTransfer.receiveObject(socket); - - if(res.sucess) { - HashtoFD.put(f.getFileHash() , f); - HashtoPath.put(f.getFileHash(), path); - System.out.println("SuccessFully Uploaded the File"); - } - } catch (IOException | ClassNotFoundException e) { - - // TODO Auto-generated catch block - e.printStackTrace(); - } - - - - } - - - - public static Node getClient() { - return client; - } - - public static void setClient(Node c) { - Handshake.client = c; - } - - public static void setCentralRegistry(Node c) { - Handshake.CentralRegistry = c; - } - - public static Node getCentralRegistry() { - - return Handshake.CentralRegistry; + + // Register file with Central Registry + public static void registerFile(FileData f, String path, String passkey) { + Node central = getCentralRegistry(); + CentralRegistryRequest req = new CentralRegistryRequest(f, getClient()); + + try { + Socket socket = new Socket(central.getPeerIP(), central.getPeerPort()); + ObjectTransfer.sendObject(socket, req); + + CentralRegistryResponse res = (CentralRegistryResponse) ObjectTransfer.receiveObject(socket); + + if (res.sucess) { + HashtoFD.put(f.getFileHash(), f); + HashtoPath.put(f.getFileHash(), path); + HashtoPasskey.put(f.getFileHash(), passkey); + + System.out.println("Successfully uploaded file: " + path); + } + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); } - -} \ No newline at end of file +} + + + public static Node getClient() { return client; } + public static void setClient(Node c) { client = c; } + public static Node getCentralRegistry() { return CentralRegistry; } + public static void setCentralRegistry(Node c) { CentralRegistry = c; } +} diff --git a/src/downloads/samp.txt b/src/downloads/samp.txt new file mode 100644 index 0000000..685e514 --- /dev/null +++ b/src/downloads/samp.txt @@ -0,0 +1 @@ +Hi I am Shreyash Kumar DUbe.This is the second time Sending this to peer 2. diff --git a/src/downloads/sample.txt b/src/downloads/sample.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/downloads/sample2.txt b/src/downloads/sample2.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/p2p/ConnectionHandlerSequential.java b/src/p2p/ConnectionHandlerSequential.java index 5dd3950..7d43daf 100644 --- a/src/p2p/ConnectionHandlerSequential.java +++ b/src/p2p/ConnectionHandlerSequential.java @@ -1,149 +1,136 @@ package p2p; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.net.ServerSocket; import java.net.Socket; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; - import javax.crypto.Cipher; import javax.crypto.KeyGenerator; -import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import utils.CryptoUtils; import utils.UserExperience; public class ConnectionHandlerSequential { - static int CHUNK_SIZE = 20; - - - - - public static void sendFile(String serverAddress, int port, String filePath, PublicKey pub) { - try (Socket socket = new Socket(serverAddress, port); - DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); - FileInputStream fileInputStream = new FileInputStream(filePath)) { - - - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - keyGenerator.init(256); // 256-bit key - SecretKey aesKey = keyGenerator.generateKey(); - - - Cipher rsaCipher = Cipher.getInstance("RSA"); - rsaCipher.init(Cipher.ENCRYPT_MODE, pub); - byte[] encryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded()); - - dataOutputStream.writeInt(encryptedAesKey.length); // Send key length - dataOutputStream.write(encryptedAesKey); // Send encrypted key - - Cipher aesCipher = Cipher.getInstance("AES"); - aesCipher.init(Cipher.ENCRYPT_MODE, aesKey); - - File file = new File(filePath); - long fileSize = file.length(); - dataOutputStream.writeLong(fileSize); - System.out.println("Recieving file "); - // Encrypt and send file in chunks - byte[] buffer = new byte[4096]; // File read buffer - int bytesRead; - int totalBytes = 0; - while ((bytesRead = fileInputStream.read(buffer)) != -1) { - byte[] encryptedChunk = aesCipher.update(buffer, 0, bytesRead); - if (encryptedChunk != null) { - dataOutputStream.write(encryptedChunk); - } - totalBytes += bytesRead; - UserExperience.printProgressBar(totalBytes , fileSize); - } - - // Finalize encryption - byte[] finalEncryptedChunk = aesCipher.doFinal(); - if (finalEncryptedChunk != null) { - dataOutputStream.write(finalEncryptedChunk); - } - UserExperience.printProgressBar(fileSize , fileSize); - System.out.println("\n File sent successfully."); - - } catch (Exception e) { - System.err.println("Error during file transfer: " + e.getMessage()); - e.printStackTrace(); - } - } - - - - - public static void receiveFile(int port, String fileName, PrivateKey prv) { - try (ServerSocket serverSocket = new ServerSocket(port)) { - System.out.println("Server started on port " + port); - - // Accept the client connection - try (Socket clientSocket = serverSocket.accept(); - DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream()); - FileOutputStream fileOutputStream = new FileOutputStream(fileName)) { - - int encryptedKeyLength = dataInputStream.readInt(); - byte[] encryptedAesKey = new byte[encryptedKeyLength]; - dataInputStream.readFully(encryptedAesKey); - - - Cipher rsaCipher = Cipher.getInstance("RSA"); - rsaCipher.init(Cipher.DECRYPT_MODE, prv); - byte[] aesKeyBytes = rsaCipher.doFinal(encryptedAesKey); - SecretKey aesKey = new SecretKeySpec(aesKeyBytes, "AES"); - - - Cipher aesCipher = Cipher.getInstance("AES"); - aesCipher.init(Cipher.DECRYPT_MODE, aesKey); - - - long fileSize = dataInputStream.readLong(); - - - System.out.println("Initiating File Transfer"); - // Decrypt and write file in chunks - byte[] buffer = new byte[4096]; // File write buffer - int bytesRead; - long totalBytesRead = 0; - while (totalBytesRead < fileSize) { - bytesRead = dataInputStream.read(buffer); - if (bytesRead == -1) break; - byte[] decryptedChunk = aesCipher.update(buffer, 0, bytesRead); - if (decryptedChunk != null) { - fileOutputStream.write(decryptedChunk); - } - totalBytesRead += bytesRead; - UserExperience.printProgressBar(totalBytesRead, fileSize); - } - - // Finalize decryption - byte[] finalDecryptedChunk = aesCipher.doFinal(); - if (finalDecryptedChunk != null) { - fileOutputStream.write(finalDecryptedChunk); - } - UserExperience.printProgressBar(fileSize , fileSize); - System.out.println("\n File received and decrypted successfully."); - - } catch (Exception e) { - System.err.println("Error during file transfer: " + e.getMessage()); - e.printStackTrace(); - } - } catch (IOException e) { - System.err.println("Could not start server on port " + port + ": " + e.getMessage()); - e.printStackTrace(); - } - } - } - + // Sends a file to a peer with AES-GCM encryption protected by passkey + public static void sendFile(String serverAddress, int port, String filePath, Object pub, String passkey) { + try (Socket socket = new Socket(serverAddress, port); + DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); + FileInputStream fileInputStream = new FileInputStream(filePath)) { + + // Generate AES key for file encryption + KeyGenerator keyGen = KeyGenerator.getInstance("AES"); + keyGen.init(256); + SecretKey fileKey = keyGen.generateKey(); + + // Encrypt AES key using passkey + byte[] salt = CryptoUtils.randomBytes(16); + SecretKey passKeyDerived = CryptoUtils.deriveKey(passkey.toCharArray(), salt); + byte[] iv = CryptoUtils.randomBytes(12); // AES-GCM IV + byte[] encFileKey = CryptoUtils.aesGcmEncrypt(fileKey.getEncoded(), passKeyDerived, iv, null); + + // Send salt, IV, and encrypted AES key + dataOutputStream.writeInt(salt.length); + dataOutputStream.write(salt); + dataOutputStream.writeInt(iv.length); + dataOutputStream.write(iv); + dataOutputStream.writeInt(encFileKey.length); + dataOutputStream.write(encFileKey); + + // Encrypt and send file + Cipher aesCipher = Cipher.getInstance("AES"); + aesCipher.init(Cipher.ENCRYPT_MODE, fileKey); + + File file = new File(filePath); + long fileSize = file.length(); + dataOutputStream.writeLong(fileSize); + + byte[] buffer = new byte[4096]; + int bytesRead; + long totalBytes = 0; + + while ((bytesRead = fileInputStream.read(buffer)) != -1) { + byte[] encryptedChunk = aesCipher.update(buffer, 0, bytesRead); + if (encryptedChunk != null) dataOutputStream.write(encryptedChunk); + totalBytes += bytesRead; + UserExperience.printProgressBar(totalBytes, fileSize); + } + + byte[] finalChunk = aesCipher.doFinal(); + if (finalChunk != null) dataOutputStream.write(finalChunk); + + UserExperience.printProgressBar(fileSize, fileSize); + System.out.println("\nFile sent successfully with passkey protection!"); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Receives a file from a peer and decrypts it using AES-GCM with passkey + public static void receiveFile(int port, String filePath, String passkey) { + try (ServerSocket serverSocket = new ServerSocket(port)) { + System.out.println("Server started on port " + port); + + try (Socket clientSocket = serverSocket.accept(); + DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream())) { + + // Ensure downloads directory exists + File outFile = new File(filePath); + outFile.getParentFile().mkdirs(); + + try (FileOutputStream fileOutputStream = new FileOutputStream(outFile)) { + + // Read salt, IV, and encrypted AES key + int saltLen = dataInputStream.readInt(); + byte[] salt = new byte[saltLen]; + dataInputStream.readFully(salt); + + int ivLen = dataInputStream.readInt(); + byte[] iv = new byte[ivLen]; + dataInputStream.readFully(iv); + + int encKeyLen = dataInputStream.readInt(); + byte[] encKey = new byte[encKeyLen]; + dataInputStream.readFully(encKey); + + // Derive AES key from passkey and decrypt file AES key + SecretKey derivedKey = CryptoUtils.deriveKey(passkey.toCharArray(), salt); + byte[] fileKeyBytes = CryptoUtils.aesGcmDecrypt(encKey, derivedKey, iv, null); + SecretKey fileKey = new SecretKeySpec(fileKeyBytes, "AES"); + + // Prepare AES cipher for file decryption + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, fileKey); + + // Read file data + long fileSize = dataInputStream.readLong(); + byte[] buffer = new byte[4096]; + long totalRead = 0; + + while (totalRead < fileSize) { + int read = dataInputStream.read(buffer); + if (read == -1) break; + byte[] decrypted = cipher.update(buffer, 0, read); + if (decrypted != null) fileOutputStream.write(decrypted); + totalRead += read; + UserExperience.printProgressBar(totalRead, fileSize); + } + + byte[] finalBytes = cipher.doFinal(); + if (finalBytes != null) fileOutputStream.write(finalBytes); + UserExperience.printProgressBar(fileSize, fileSize); + + System.out.println("\nFile received and decrypted successfully!"); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/p2p/FileReciever.java b/src/p2p/FileReciever.java index a08d77d..e282874 100644 --- a/src/p2p/FileReciever.java +++ b/src/p2p/FileReciever.java @@ -2,91 +2,75 @@ import java.io.IOException; import java.net.Socket; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; import discovery.FileData; import discovery.Handshake; import discovery.Node; -import discovery.messages.*; -import java.io.File; -import utils.Config; +import discovery.messages.CentralRegistryRequest; +import discovery.messages.CentralRegistryResponse; +import discovery.messages.FileRequest; +import discovery.messages.FileResponse; +import discovery.messages.TransferRequest; + public class FileReciever { - // this class has all the methods for the file reciever - public static void downloadFile(String fileHash , Node CentralRegistry) { - - - - CentralRegistryRequest req = new CentralRegistryRequest(fileHash); - - try { - Socket socket = new Socket(CentralRegistry.getPeerIP() , CentralRegistry.getPeerPort()); - ObjectTransfer.sendObject(socket , req); - Object obj = ObjectTransfer.receiveObject(socket); - CentralRegistryResponse res = (CentralRegistryResponse)obj; - FileData file = new FileData(); - file.setFileHash(fileHash); - if(res.sucess) { - for(Node potentialPeer : res.peers) { - - //implement a peer selection logic here - if(FileReciever.downloadFromPeer(potentialPeer , file)) { - System.out.println("\n Downloaded from peer " + potentialPeer); - - break; - } - else { - System.out.println("Failed Downloading from " + potentialPeer); - } - } - } - else { - System.out.println("FAILED TO GET PEERS"); - } - } catch (IOException | ClassNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - public static boolean downloadFromPeer(Node peer , FileData f) { - Node two = Handshake.getClient(); - FileRequest req = new FileRequest(f , two); - - - - - - try (Socket socket = new Socket(peer.getPeerIP(), peer.getPeerPort())) { - - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); // Key size - KeyPair keys = keyPairGenerator.generateKeyPair(); - req.pub = keys.getPublic(); - - - ObjectTransfer.sendObject(socket, req); - - Object obj = ObjectTransfer.receiveObject(socket); - - FileResponse res = (FileResponse)obj; - String fileName = res.file.getFileName(); - TransferRequest treq = new TransferRequest(two , f , 7777); - - ObjectTransfer.sendObject(socket, treq); - - - String filePath = new File(Config.getDownloadsDir(), fileName).getPath(); - - ConnectionHandlerSequential.receiveFile(treq.Port , filePath , keys.getPrivate()); - - Handshake.registerFile(f , filePath); - return true; - } catch (IOException | ClassNotFoundException | NoSuchAlgorithmException e) { - e.printStackTrace(); - return false; - } - } + // Download a file by hash from Central Registry + public static void downloadFile(String fileHash, Node centralRegistry, String passkey) { + CentralRegistryRequest req = new CentralRegistryRequest(fileHash); + + try { + Socket socket = new Socket(centralRegistry.getPeerIP(), centralRegistry.getPeerPort()); + ObjectTransfer.sendObject(socket, req); + Object obj = ObjectTransfer.receiveObject(socket); + CentralRegistryResponse res = (CentralRegistryResponse) obj; + + FileData file = new FileData(); + file.setFileHash(fileHash); + + if (res.sucess) { + for (Node peerNode : res.peers) { + if (downloadFromPeer(peerNode, file, passkey)) { + System.out.println("\nDownloaded from peer: " + peerNode); + break; + } else { + System.out.println("Failed downloading from peer: " + peerNode); + } + } + } else { + System.out.println("FAILED TO GET PEERS"); + } + + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + } + + public static boolean downloadFromPeer(Node peer, FileData file, String passkey) { + Node client = Handshake.getClient(); + FileRequest req = new FileRequest(file, client); + + try (Socket socket = new Socket(peer.getPeerIP(), peer.getPeerPort())) { + // Send request to peer + ObjectTransfer.sendObject(socket, req); + Object obj = ObjectTransfer.receiveObject(socket); + FileResponse res = (FileResponse) obj; + + String fileName = res.file.getFileName(); + TransferRequest treq = new TransferRequest(client, file, 7777); + ObjectTransfer.sendObject(socket, treq); + + String filePath = "./downloads/" + fileName; + + // Receive file using passkey + ConnectionHandlerSequential.receiveFile(treq.Port, filePath, passkey); + + // Register file locally + Handshake.registerFile(file, filePath, passkey); + + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } } diff --git a/src/testing/FileTesting.java b/src/testing/FileTesting.java index 35e71e9..eaa6355 100644 --- a/src/testing/FileTesting.java +++ b/src/testing/FileTesting.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Scanner; import discovery.CentralRegistry; import discovery.FileData; @@ -13,38 +14,32 @@ import p2p.FileReciever; public class FileTesting { - public static String CentralIP = "127.0.0.1"; - public static String SenderIP = "127.0.0.1"; - public static String RecieverIP = "127.0.0.1"; - public static int CentralPort = 3000; - public static int SenderPort = 4000; - public static int RecieverPort = 5000; - public static void test(String []files) { - // this gives an array of file paths - - String[] hashes = new String[files.length]; + + public static String CentralIP = "127.0.0.1"; + public static String SenderIP = "127.0.0.1"; + public static String RecieverIP = "127.0.0.1"; + public static int CentralPort = 3000; + public static int SenderPort = 4000; + public static int RecieverPort = 5000; + + public static void test(String[] files) { + String[] hashes = new String[files.length]; for (int i = 0; i < files.length; i++) { hashes[i] = generateHash(files[i]); } - - Thread recieverThread = new Thread(() ->{ - recieverThread(hashes); - }); - Thread senderThread = new Thread(() -> { - senderThread(files,recieverThread); - - }); - Thread centralThread = new Thread(() -> { - centralThread(); - }); + + Thread recieverThread = new Thread(() -> recieverThread(hashes)); + Thread senderThread = new Thread(() -> senderThread(files, recieverThread)); + Thread centralThread = new Thread(FileTesting::centralThread); + centralThread.start(); senderThread.start(); + } - } - public static String generateHash(String filePath) { - File file = new File(filePath); - try (FileInputStream fis = new FileInputStream(file)) { + public static String generateHash(String filePath) { + File file = new File(filePath); + try (FileInputStream fis = new FileInputStream(file)) { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[8192]; int bytesRead; @@ -52,61 +47,72 @@ public static String generateHash(String filePath) { md.update(buffer, 0, bytesRead); } byte[] hashBytes = md.digest(); - - // Convert the byte array to a hexadecimal string StringBuilder sb = new StringBuilder(); - for (byte b : hashBytes) { - sb.append(String.format("%02x", b)); - } + for (byte b : hashBytes) sb.append(String.format("%02x", b)); return sb.toString(); } catch (NoSuchAlgorithmException | IOException e) { - throw new RuntimeException("MD5 algorithm not found.", e); + throw new RuntimeException("Failed to generate hash for file: " + filePath, e); } - - } - public static void centralThread() { - Node central = new Node(); - central.setPeerIP(CentralIP); - central.setPeerPort(CentralPort); - CentralRegistry.start(central); - } - public static void senderThread(String []files , Thread recieverThread) { - Node client = new Node(); - client.setPeerIP(SenderIP); - client.setPeerPort(SenderPort); - - Node central = new Node(); - central.setPeerIP(CentralIP); - central.setPeerPort(CentralPort); - try { - Handshake.setClient(client); - Handshake.setCentralRegistry(central); - Thread t = new Thread(() -> Handshake.start(client.getPeerPort() , client)); - t.start(); //this thread line will be started for each of the peer - for(String filePath : files) { - FileData f = new FileData(filePath); - Handshake.registerFile(f , filePath); + } + + public static void centralThread() { + Node central = new Node(); + central.setPeerIP(CentralIP); + central.setPeerPort(CentralPort); + CentralRegistry.start(central); + } + + public static void senderThread(String[] files, Thread recieverThread) { + Node client = new Node(); + client.setPeerIP(SenderIP); + client.setPeerPort(SenderPort); + + Node central = new Node(); + central.setPeerIP(CentralIP); + central.setPeerPort(CentralPort); + + Handshake.setClient(client); + Handshake.setCentralRegistry(central); + + Thread t = new Thread(() -> Handshake.start(client.getPeerPort(), client)); + t.start(); // start handshake server + + // Ask user for passkey + Scanner sc = new Scanner(System.in); + System.out.print("Enter passkey for these file(s): "); + String passkey = sc.nextLine(); + + for (String filePath : files) { + try { + File fCheck = new File(filePath.trim()); + if (!fCheck.exists()) { + System.out.println("File not found: " + filePath); + continue; + } + + FileData f = new FileData(filePath.trim()); + Handshake.registerFile(f, filePath.trim(), passkey); + System.out.println("File registered: " + filePath); + } catch (IOException e) { + System.out.println("Failed to read file: " + filePath); + e.printStackTrace(); } - recieverThread.start(); } - catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - public static void recieverThread(String []hashes) { - Node client = new Node(); - client.setPeerIP(SenderIP); - client.setPeerPort(SenderPort); - - Node central = new Node(); - central.setPeerIP(CentralIP); - central.setPeerPort(CentralPort); - - for(String hash : hashes) { - FileReciever.downloadFile(hash, central); - } - - } + + recieverThread.start(); + } + + public static void recieverThread(String[] hashes) { + Node central = new Node(); + central.setPeerIP(CentralIP); + central.setPeerPort(CentralPort); + + Scanner sc = new Scanner(System.in); + + for (String hash : hashes) { + System.out.print("Enter passkey to download file with hash " + hash + ": "); + String passkey = sc.nextLine(); + FileReciever.downloadFile(hash, central, passkey); + } + } } diff --git a/src/utils/CryptoUtils.java b/src/utils/CryptoUtils.java new file mode 100644 index 0000000..cb66907 --- /dev/null +++ b/src/utils/CryptoUtils.java @@ -0,0 +1,47 @@ +package utils; + +import javax.crypto.*; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; +import java.util.Base64; + +public class CryptoUtils { + private static final int KEY_SIZE = 256; + private static final int IV_SIZE = 12; + private static final int TAG_LENGTH = 128; + private static final int ITERATIONS = 100_000; + + public static SecretKey deriveKey(char[] passphrase, byte[] salt) throws Exception { + PBEKeySpec spec = new PBEKeySpec(passphrase, salt, ITERATIONS, KEY_SIZE); + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + byte[] keyBytes = factory.generateSecret(spec).getEncoded(); + return new SecretKeySpec(keyBytes, "AES"); + } + + public static byte[] aesGcmEncrypt(byte[] plaintext, SecretKey key, byte[] iv, byte[] aad) throws Exception { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH, iv); + cipher.init(Cipher.ENCRYPT_MODE, key, spec); + if(aad != null) cipher.updateAAD(aad); + return cipher.doFinal(plaintext); + } + + public static byte[] aesGcmDecrypt(byte[] ciphertext, SecretKey key, byte[] iv, byte[] aad) throws Exception { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH, iv); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + if(aad != null) cipher.updateAAD(aad); + return cipher.doFinal(ciphertext); + } + + public static byte[] randomBytes(int length) { + byte[] b = new byte[length]; + new SecureRandom().nextBytes(b); + return b; + } + + public static String toBase64(byte[] data) { return Base64.getEncoder().encodeToString(data); } + public static byte[] fromBase64(String s) { return Base64.getDecoder().decode(s); } +} From 5fe8cbf426776dc7def23c16d72569bf5dd4f946 Mon Sep 17 00:00:00 2001 From: ShreyashkumarDube <86827868+ShreyashkumarDube@users.noreply.github.com> Date: Sat, 4 Oct 2025 14:32:12 +0530 Subject: [PATCH 2/2] Fix typo in FileReceiver and update implementation --- src/p2p/FileReciever.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p/FileReciever.java b/src/p2p/FileReciever.java index e282874..a95877a 100644 --- a/src/p2p/FileReciever.java +++ b/src/p2p/FileReciever.java @@ -73,4 +73,4 @@ public static boolean downloadFromPeer(Node peer, FileData file, String passkey) return false; } } -} +}