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 0399247..cf50ecd 100644 --- a/src/discovery/Handshake.java +++ b/src/discovery/Handshake.java @@ -1,120 +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; -import utils.UserExperience; -import utils.UserExperience.TransferStage; public class Handshake { private static Node client; private static Node CentralRegistry; - + + // 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) { - System.out.println("Peer listening on port: " + port); - 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); - - UserExperience.printStatus("Incoming file request received"); - - 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())); - - UserExperience.printStatus("File found, sending response..."); - ObjectTransfer.sendObject(socket, res); - - obj = ObjectTransfer.receiveObject(socket); - TransferRequest treq = (TransferRequest)obj; - - TransferResponse tres = new TransferResponse(treq.RequestingNode, client, treq.Port, true); - ObjectTransfer.sendObject(socket, tres); - - socket.close(); - - UserExperience.printSeparator(); - UserExperience.printStatus("Starting file transfer to " + req.RequestingNode.getPeerIP()); - - ConnectionHandlerSequential.sendFile( - tres.RequestingNode.getPeerIP(), - tres.Port, - Handshake.HashtoPath.get(treq.Fd.getFileHash()), - req.pub - ); + + 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) { - Node central = Handshake.getCentralRegistry(); - Node c = Handshake.getClient(); - CentralRegistryRequest req = new CentralRegistryRequest(f, c); - - UserExperience.printSeparator(); - UserExperience.printStatus("Registering file with central registry..."); - UserExperience.printStatus("File: " + f.getFileName()); - UserExperience.printStatus("Hash: " + f.getFileHash()); - UserExperience.printStatus("Size: " + UserExperience.formatBytes(f.getFileSize())); - - 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); - UserExperience.printSuccess("File registered successfully!"); - UserExperience.printStatus("Your file is now available for download"); - } else { - UserExperience.printWarning("Failed to register file"); - } - UserExperience.printSeparator(); - - socket.close(); - } catch (IOException | ClassNotFoundException e) { - UserExperience.printWarning("Error contacting central registry: " + e.getMessage()); - e.printStackTrace(); + + // 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(); } - - 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; - } -} \ 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 e9a54f6..49c6f11 100644 --- a/src/p2p/ConnectionHandlerSequential.java +++ b/src/p2p/ConnectionHandlerSequential.java @@ -1,158 +1,136 @@ package p2p; -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.PrivateKey; -import java.security.PublicKey; - import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import utils.CryptoUtils; import utils.UserExperience; import utils.UserExperience.TransferStage; public class ConnectionHandlerSequential { - static int CHUNK_SIZE = 20; - - public static void sendFile(String serverAddress, int port, String filePath, PublicKey pub) { - try { - UserExperience.printStage(TransferStage.CONNECTING); - Socket socket = new Socket(serverAddress, port); - DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream()); - FileInputStream fileInputStream = new FileInputStream(filePath); - - UserExperience.printStage(TransferStage.ENCRYPTING); - UserExperience.printStatus("Generating AES encryption key..."); - - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - keyGenerator.init(256); - 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); - dataOutputStream.write(encryptedAesKey); - - UserExperience.printStatus("Encryption key exchanged successfully"); + // 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, aesKey); + aesCipher.init(Cipher.ENCRYPT_MODE, fileKey); File file = new File(filePath); long fileSize = file.length(); dataOutputStream.writeLong(fileSize); - - UserExperience.printStage(TransferStage.TRANSFERRING); - UserExperience.printStatus("Sending file: " + file.getName() + " (" + UserExperience.formatBytes(fileSize) + ")"); - - // Encrypt and send file in chunks + byte[] buffer = new byte[4096]; int bytesRead; - int totalBytes = 0; + long totalBytes = 0; + while ((bytesRead = fileInputStream.read(buffer)) != -1) { byte[] encryptedChunk = aesCipher.update(buffer, 0, bytesRead); - if (encryptedChunk != null) { - dataOutputStream.write(encryptedChunk); - } + if (encryptedChunk != null) dataOutputStream.write(encryptedChunk); totalBytes += bytesRead; UserExperience.printProgressBar(totalBytes, fileSize); } - // Finalize encryption - UserExperience.printStage(TransferStage.FINALIZING); - byte[] finalEncryptedChunk = aesCipher.doFinal(); - if (finalEncryptedChunk != null) { - dataOutputStream.write(finalEncryptedChunk); - } + byte[] finalChunk = aesCipher.doFinal(); + if (finalChunk != null) dataOutputStream.write(finalChunk); + UserExperience.printProgressBar(fileSize, fileSize); - - UserExperience.printStage(TransferStage.COMPLETE); - UserExperience.printSuccess("File sent successfully!"); - - fileInputStream.close(); - socket.close(); + System.out.println("\nFile sent successfully with passkey protection!"); } 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 { - UserExperience.printStage(TransferStage.CONNECTING); - UserExperience.printStatus("Waiting for sender on port " + port + "..."); - - ServerSocket serverSocket = new ServerSocket(port); - Socket clientSocket = serverSocket.accept(); - - UserExperience.printSuccess("Connected to sender"); - UserExperience.printStage(TransferStage.DECRYPTING); - - DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream()); - FileOutputStream fileOutputStream = new FileOutputStream(fileName); - - UserExperience.printStatus("Receiving encryption key..."); - 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(); - - UserExperience.printSuccess("Decryption key received"); - UserExperience.printStage(TransferStage.TRANSFERRING); - UserExperience.printStatus("Receiving file (" + UserExperience.formatBytes(fileSize) + ")..."); - - // Decrypt and write file in chunks - byte[] buffer = new byte[4096]; - 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); + // 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!"); } - totalBytesRead += bytesRead; - UserExperience.printProgressBar(totalBytesRead, fileSize); - } - // Finalize decryption - UserExperience.printStage(TransferStage.FINALIZING); - byte[] finalDecryptedChunk = aesCipher.doFinal(); - if (finalDecryptedChunk != null) { - fileOutputStream.write(finalDecryptedChunk); + } catch (Exception e) { + e.printStackTrace(); } - UserExperience.printProgressBar(fileSize, fileSize); - - UserExperience.printStage(TransferStage.COMPLETE); - UserExperience.printSuccess("File received and saved: " + fileName); - - fileOutputStream.close(); - clientSocket.close(); - serverSocket.close(); - } catch (Exception e) { - System.err.println("Error during file transfer: " + e.getMessage()); + } catch (IOException e) { e.printStackTrace(); } } diff --git a/src/p2p/FileReciever.java b/src/p2p/FileReciever.java index 9f64a9f..a95877a 100644 --- a/src/p2p/FileReciever.java +++ b/src/p2p/FileReciever.java @@ -2,114 +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 utils.UserExperience; -import utils.UserExperience.TransferStage; +import discovery.messages.CentralRegistryRequest; +import discovery.messages.CentralRegistryResponse; +import discovery.messages.FileRequest; +import discovery.messages.FileResponse; +import discovery.messages.TransferRequest; public class FileReciever { - - public static void downloadFile(String fileHash, Node CentralRegistry) { - - UserExperience.printSeparator(); - UserExperience.printStage(TransferStage.CONNECTING); - UserExperience.printStatus("Contacting central registry for file: " + fileHash); - + + // 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()); + Socket socket = new Socket(centralRegistry.getPeerIP(), centralRegistry.getPeerPort()); ObjectTransfer.sendObject(socket, req); - - UserExperience.printStage(TransferStage.REQUESTING); - UserExperience.printStatus("Requesting peer list from registry..."); - Object obj = ObjectTransfer.receiveObject(socket); - CentralRegistryResponse res = (CentralRegistryResponse)obj; + CentralRegistryResponse res = (CentralRegistryResponse) obj; + FileData file = new FileData(); file.setFileHash(fileHash); - + if (res.sucess) { - UserExperience.printSuccess("Found " + res.peers.length + " peer(s) with the file"); - - boolean downloaded = false; - for (int i = 0; i < res.peers.length; i++) { - Node potentialPeer = res.peers[i]; - - UserExperience.printSeparator(); - UserExperience.printStatus("Attempting download from peer " + (i+1) + "/" + res.peers.length); - UserExperience.printStatus("Peer: " + potentialPeer.getPeerIP() + ":" + potentialPeer.getPeerPort()); - - if (FileReciever.downloadFromPeer(potentialPeer, file)) { - UserExperience.printSuccess("Download completed successfully!"); - downloaded = true; + for (Node peerNode : res.peers) { + if (downloadFromPeer(peerNode, file, passkey)) { + System.out.println("\nDownloaded from peer: " + peerNode); break; } else { - UserExperience.printWarning("Failed to download from peer " + (i+1)); + System.out.println("Failed downloading from peer: " + peerNode); } } - - if (!downloaded) { - UserExperience.printWarning("Could not download from any peer"); - } - UserExperience.printSeparator(); } else { - UserExperience.printWarning("No peers found with this file"); - UserExperience.printSeparator(); + System.out.println("FAILED TO GET PEERS"); } - - socket.close(); + } catch (IOException | ClassNotFoundException e) { - System.err.println("Error contacting registry: " + e.getMessage()); e.printStackTrace(); } } - - public static boolean downloadFromPeer(Node peer, FileData f) { - Node two = Handshake.getClient(); - FileRequest req = new FileRequest(f, two); - - try { - UserExperience.printStage(TransferStage.NEGOTIATING); - UserExperience.printStatus("Establishing secure connection..."); - - Socket socket = new Socket(peer.getPeerIP(), peer.getPeerPort()); - - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(2048); - KeyPair keys = keyPairGenerator.generateKeyPair(); - req.pub = keys.getPublic(); - - UserExperience.printStatus("Sending file request..."); + + 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; + FileResponse res = (FileResponse) obj; + String fileName = res.file.getFileName(); - - UserExperience.printSuccess("Peer accepted request for: " + fileName); - - TransferRequest treq = new TransferRequest(two, f, 7777); + TransferRequest treq = new TransferRequest(client, file, 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) { - UserExperience.printWarning("Connection failed: " + e.getMessage()); + 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; } } -} \ No newline at end of file +} 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); } +}