diff --git a/bundle-core/src/main/java/net/discdd/client/applicationdatamanager/ApplicationDataManager.java b/bundle-core/src/main/java/net/discdd/client/applicationdatamanager/ApplicationDataManager.java index 004790c451..fbed6c48e6 100644 --- a/bundle-core/src/main/java/net/discdd/client/applicationdatamanager/ApplicationDataManager.java +++ b/bundle-core/src/main/java/net/discdd/client/applicationdatamanager/ApplicationDataManager.java @@ -5,6 +5,7 @@ import com.google.gson.reflect.TypeToken; import net.discdd.model.ADU; import net.discdd.model.UncompressedPayload; +import net.discdd.pathutils.ClientPaths; import net.discdd.utils.BundleUtils; import net.discdd.utils.StoreADUs; import net.discdd.utils.StreamExt; @@ -15,7 +16,6 @@ import java.io.IOException; import java.lang.reflect.Type; import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -38,27 +38,22 @@ public class ApplicationDataManager { private StoreADUs receiveADUsStorage; private Consumer aduConsumer; /* Database tables */ - private static String SENT_BUNDLE_DETAILS = "Shared/DB/SENT_BUNDLE_DETAILS.json"; - - private static String LAST_SENT_BUNDLE_STRUCTURE = "Shared/DB/LAST_SENT_BUNDLE_STRUCTURE.json"; - - private Long APP_DATA_SIZE_LIMIT = 1000000000L; private static List REGISTER_APP_IDS = List.of("com.example.mysignal", "com.fsck.k9.debug", "testAppId"); - private final Path ROOT_DIR; + private ClientPaths clientPaths; - public ApplicationDataManager(Path rootDir, Consumer aduConsumer) { - ROOT_DIR = rootDir; - sendADUsStorage = new StoreADUs(rootDir.resolve("send")); - receiveADUsStorage = new StoreADUs(rootDir.resolve("receive")); + public ApplicationDataManager(ClientPaths clientPaths, Consumer aduConsumer) { + this.clientPaths = clientPaths; + sendADUsStorage = new StoreADUs(clientPaths.sendADUsPath); + receiveADUsStorage = new StoreADUs(clientPaths.receiveADUsPath); this.aduConsumer = aduConsumer; try { - File sentBundleDetails = ROOT_DIR.resolve(SENT_BUNDLE_DETAILS).toFile(); + File sentBundleDetails = clientPaths.sendBundleDetailsPath.toFile(); sentBundleDetails.createNewFile(); - File lastSentBundleStructure = ROOT_DIR.resolve(LAST_SENT_BUNDLE_STRUCTURE).toFile(); + File lastSentBundleStructure = clientPaths.lastSentBundleStructurePath.toFile(); lastSentBundleStructure.createNewFile(); } catch (IOException e) { e.printStackTrace(); @@ -109,7 +104,7 @@ public void storeReceivedADUs(String clientId, String bundleId, List adus) public List fetchADUsToSend(long initialSize, String clientId) throws IOException { List adusToSend = new ArrayList<>(); - final long dataSizeLimit = this.APP_DATA_SIZE_LIMIT; + final long dataSizeLimit = clientPaths.APP_DATA_SIZE_LIMIT; var sizeLimiter = new SizeLimiter(dataSizeLimit - initialSize); for (String appId : this.getRegisteredAppIds()) { StreamExt.takeWhile(sendADUsStorage.getADUs(clientId, appId), a -> sizeLimiter.test(a.getSize())) @@ -141,17 +136,17 @@ public void notifyBundleSent(UncompressedPayload bundle) { } public Optional getLastSentBundleBuilder() { - return BundleUtils.jsonToBundleBuilder(ROOT_DIR.resolve(LAST_SENT_BUNDLE_STRUCTURE).toFile()); + return BundleUtils.jsonToBundleBuilder(clientPaths.lastSentBundleStructurePath.toFile()); } private void writeLastSentBundleStructure(UncompressedPayload lastSentBundle) { - BundleUtils.writeBundleStructureToJson(lastSentBundle, ROOT_DIR.resolve(LAST_SENT_BUNDLE_STRUCTURE).toFile()); + BundleUtils.writeBundleStructureToJson(lastSentBundle,clientPaths.lastSentBundleStructurePath.toFile()); } private void writeSentBundleDetails(Map> sentBundleDetails) { Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jsonString = gson.toJson(sentBundleDetails); - try (FileWriter writer = new FileWriter(ROOT_DIR.resolve(SENT_BUNDLE_DETAILS).toFile())) { + try (FileWriter writer = new FileWriter(clientPaths.sendBundleDetailsPath.toFile())) { writer.write(jsonString); } catch (IOException e) { e.printStackTrace(); @@ -179,7 +174,7 @@ public boolean test(Long size) { private Map> getSentBundleDetails() { Gson gson = new Gson(); Map> ret = new HashMap<>(); - try (FileReader reader = new FileReader(ROOT_DIR.resolve(SENT_BUNDLE_DETAILS).toFile())) { + try (FileReader reader = new FileReader(clientPaths.sendBundleDetailsPath.toFile())) { Type mapType = new TypeToken>>() {}.getType(); ret = gson.fromJson(reader, mapType); if (ret == null) { diff --git a/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientBundleGenerator.java b/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientBundleGenerator.java index 96f15f0bed..c525f6fc4c 100644 --- a/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientBundleGenerator.java +++ b/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientBundleGenerator.java @@ -1,22 +1,19 @@ package net.discdd.client.bundlerouting; -import static java.util.logging.Level.WARNING; - import net.discdd.bundlesecurity.BundleIDGenerator; - import net.discdd.client.bundlesecurity.ClientSecurity; - +import net.discdd.pathutils.ClientPaths; import org.whispersystems.libsignal.InvalidKeyException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.util.Objects; import java.util.logging.Logger; +import static java.util.logging.Level.WARNING; + public class ClientBundleGenerator { private static final Logger logger = Logger.getLogger(ClientBundleGenerator.class.getName()); @@ -27,14 +24,14 @@ public class ClientBundleGenerator { /* Counter value used as unsigned long */ private long currentCounter = 0; - final private Path counterFilePath; + private ClientPaths clientPaths; - private ClientBundleGenerator(ClientSecurity clientSecurity, Path rootPath) throws IOException { + private ClientBundleGenerator(ClientSecurity clientSecurity, ClientPaths clientPaths) throws IOException { this.clientSecurity = clientSecurity; - counterFilePath = rootPath.resolve(Paths.get("BundleRouting", "sentBundle.id")); + this.clientPaths = clientPaths; try { - byte[] counterFromFile = Files.readAllBytes(counterFilePath); + byte[] counterFromFile = Files.readAllBytes(clientPaths.counterFilePath); currentCounter = Long.parseUnsignedLong(new String(counterFromFile, StandardCharsets.UTF_8)); } catch (IOException e) { updateBundleIDFile(); @@ -42,13 +39,13 @@ private ClientBundleGenerator(ClientSecurity clientSecurity, Path rootPath) thro } private void updateBundleIDFile() throws IOException { - counterFilePath.getParent().toFile().mkdirs(); - Files.write(counterFilePath, Long.toUnsignedString(currentCounter).getBytes()); + clientPaths.counterFilePath.getParent().toFile().mkdirs(); + Files.write(clientPaths.counterFilePath, Long.toUnsignedString(currentCounter).getBytes()); } - synchronized public static ClientBundleGenerator initializeInstance(ClientSecurity clientSecurity, Path rootPath) throws IOException { + synchronized public static ClientBundleGenerator initializeInstance(ClientSecurity clientSecurity, ClientPaths clientPaths) throws IOException { if (singleGeneratorInstance == null) { - singleGeneratorInstance = new ClientBundleGenerator(clientSecurity, rootPath); + singleGeneratorInstance = new ClientBundleGenerator(clientSecurity, clientPaths); } else { logger.log(WARNING, "[BR]: Client bundle generator instance is already created!"); } diff --git a/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientRouting.java b/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientRouting.java index 2fa9b3e6ae..8916b75f2a 100644 --- a/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientRouting.java +++ b/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientRouting.java @@ -7,11 +7,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.gson.JsonParseException; import net.discdd.bundlerouting.RoutingExceptions.ClientMetaDataFileException; +import net.discdd.pathutils.ClientPaths; -import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; import java.util.HashMap; import java.util.logging.Logger; @@ -23,8 +22,7 @@ public class ClientRouting { private static ClientRouting singleClientRoutingInstance = null; HashMap metadata = null; - Path metaDataPath = null; - private final String METADATAFILE = "routing.metadata"; + ClientPaths clientPaths; /* Initialize client routing score table * Reads from json file if it exists, creates a new table otherwise @@ -33,31 +31,26 @@ public class ClientRouting { * Return: * None */ - private ClientRouting(Path rootPath) throws IOException, ClientMetaDataFileException { - this.metaDataPath = rootPath.resolve("BundleRouting"); - metaDataPath.toFile().mkdirs(); - - metaDataPath = metaDataPath.resolve(METADATAFILE); - - File metadataFile = metaDataPath.toFile(); + private ClientRouting(ClientPaths clientPaths) throws IOException, ClientMetaDataFileException { + this.clientPaths = clientPaths; ObjectMapper objectMapper = new ObjectMapper(); metadata = new HashMap<>(); - if (metadataFile.exists()) { + if (clientPaths.metadataFile.exists()) { try { - metadata = objectMapper.readValue(metadataFile, new TypeReference>() {}); + metadata = objectMapper.readValue(clientPaths.metadataFile, new TypeReference>() {}); } catch (JsonParseException | JsonMappingException e) { throw new ClientMetaDataFileException("Corrupted JSON File:" + e); } } else { - objectMapper.writeValue(metadataFile, metadata); + objectMapper.writeValue(clientPaths.metadataFile, metadata); } } - public static ClientRouting initializeInstance(Path metaDataPath) throws ClientMetaDataFileException, IOException { + public static ClientRouting initializeInstance(ClientPaths clientPaths) throws ClientMetaDataFileException, IOException { if (singleClientRoutingInstance == null) { - singleClientRoutingInstance = new ClientRouting(metaDataPath); + singleClientRoutingInstance = new ClientRouting(clientPaths); } else { logger.log(INFO, "[BR]: Client Routing Instance already Exists!"); } @@ -89,10 +82,9 @@ public void updateMetaData(String senderId) throws ClientMetaDataFileException { ObjectMapper mapper = new ObjectMapper(); try { - File metadataFile = metaDataPath.toFile(); - JsonNode rootNode = mapper.readTree(metadataFile); + JsonNode rootNode = mapper.readTree(clientPaths.metadataFile); ((ObjectNode) rootNode).put(senderId, count); - mapper.writeValue(metadataFile, rootNode); + mapper.writeValue(clientPaths.metadataFile, rootNode); } catch (Exception e) { throw new ClientMetaDataFileException("Error updating Routing Meta Data:" + e); } @@ -105,6 +97,6 @@ public void updateMetaData(String senderId) throws ClientMetaDataFileException { * None */ public byte[] bundleMetaData() throws IOException { - return Files.readAllBytes(metaDataPath); + return Files.readAllBytes(clientPaths.metadataFile.toPath()); } } \ No newline at end of file diff --git a/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientWindow.java b/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientWindow.java index 98bbe42ff6..d062b11b3d 100644 --- a/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientWindow.java +++ b/bundle-core/src/main/java/net/discdd/client/bundlerouting/ClientWindow.java @@ -3,13 +3,13 @@ import net.discdd.bundlerouting.WindowUtils.WindowExceptions.BufferOverflow; import net.discdd.bundlesecurity.BundleIDGenerator; import net.discdd.client.bundlesecurity.ClientSecurity; +import net.discdd.pathutils.ClientPaths; import net.discdd.utils.Constants; import org.whispersystems.libsignal.InvalidKeyException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; -import java.nio.file.Path; import java.security.GeneralSecurityException; import java.util.LinkedList; import java.util.List; @@ -27,17 +27,14 @@ public class ClientWindow { private static final Logger logger = Logger.getLogger(ClientWindow.class.getName()); - public static final String CLIENT_WINDOW_SUBDIR = "ClientWindow"; static private ClientWindow singleClientWindowInstance = null; - final private Path clientWindowDataPath; - final private String WINDOW_FILE = "clientWindow.csv"; - record UnencryptedBundleId(String bundleId, long bundleCounter) {} private final LinkedList windowOfUnencryptedBundleIds = new LinkedList<>(); private final String clientID; private int windowLength = 10; /* Default Value */ + private final ClientPaths clientPaths; /* Generates bundleIDs for window slots * Parameter: @@ -58,9 +55,7 @@ private void fillWindow(long startCounter, int count) throws IOException { } private void updateDBWindow() throws IOException { - var dbFile = clientWindowDataPath.resolve(WINDOW_FILE); - - Files.write(dbFile, String.format(Locale.US, "%d,%d", windowOfUnencryptedBundleIds.getFirst().bundleCounter(), + Files.write(clientPaths.dbFile, String.format(Locale.US, "%d,%d", windowOfUnencryptedBundleIds.getFirst().bundleCounter(), windowOfUnencryptedBundleIds.getLast().bundleCounter()).getBytes()); logger.log(FINE, "Update window: " + windowOfUnencryptedBundleIds.getFirst().bundleCounter() + " - " + @@ -68,13 +63,13 @@ private void updateDBWindow() throws IOException { } private void initializeWindow() throws IOException { - var dbFile = clientWindowDataPath.resolve(WINDOW_FILE); + var start = 0L; windowLength = Constants.DEFAULT_WINDOW_SIZE; var end = start + windowLength - 1; try { - String dbData = new String(Files.readAllBytes(dbFile)); + String dbData = new String(Files.readAllBytes(clientPaths.dbFile)); String[] dbCSV = dbData.split(","); start = Long.parseLong(dbCSV[0]); end = Long.parseLong(dbCSV[1]); @@ -94,10 +89,9 @@ private void initializeWindow() throws IOException { * Returns: * None */ - private ClientWindow(int length, String clientID, Path rootPath) { - clientWindowDataPath = rootPath.resolve(CLIENT_WINDOW_SUBDIR); - clientWindowDataPath.toFile().mkdirs(); + private ClientWindow(int length, String clientID, ClientPaths clientPaths) { this.clientID = clientID; + this.clientPaths = clientPaths; try { initializeWindow(); @@ -111,9 +105,9 @@ private ClientWindow(int length, String clientID, Path rootPath) { } } - public static ClientWindow initializeInstance(int windowLength, String clientID, Path rootPath) throws BufferOverflow, IOException { + public static ClientWindow initializeInstance(int windowLength, String clientID, ClientPaths clientPaths) throws BufferOverflow, IOException { if (singleClientWindowInstance == null) { - singleClientWindowInstance = new ClientWindow(windowLength, clientID, rootPath); + singleClientWindowInstance = new ClientWindow(windowLength, clientID, clientPaths); } else { logger.log(INFO, "[WIN]: Client Window Instance is already initialized!"); } diff --git a/bundle-core/src/main/java/net/discdd/client/bundlesecurity/BundleSecurity.java b/bundle-core/src/main/java/net/discdd/client/bundlesecurity/BundleSecurity.java index b91638d79d..c5ba4908cf 100644 --- a/bundle-core/src/main/java/net/discdd/client/bundlesecurity/BundleSecurity.java +++ b/bundle-core/src/main/java/net/discdd/client/bundlesecurity/BundleSecurity.java @@ -5,8 +5,8 @@ import net.discdd.client.bundlerouting.ClientWindow; import net.discdd.model.Payload; import net.discdd.model.UncompressedBundle; +import net.discdd.pathutils.ClientPaths; import net.discdd.utils.Constants; -import net.discdd.utils.FileUtils; import org.whispersystems.libsignal.DuplicateMessageException; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidMessageException; @@ -27,64 +27,36 @@ public class BundleSecurity { private static final Logger logger = Logger.getLogger(BundleSecurity.class.getName()); - - public static final String BUNDLE_SECURITY_DIR = "BundleSecurity"; - private static final String LARGEST_BUNDLE_ID_RECEIVED = "Shared/DB/LARGEST_BUNDLE_ID_RECEIVED.txt"; - private static final String BUNDLE_ID_NEXT_COUNTER = "Shared/DB/BUNDLE_ID_NEXT_COUNTER.txt"; - public static final String SERVER_IDENTITY_PUB = "server_identity.pub"; - public static final String SERVER_SIGNED_PRE_PUB = "server_signed_pre.pub"; - public static final String SERVER_RATCHET_PUB = "server_ratchet.pub"; - public static final String SERVER_KEYS_SUBDIR = "Server_Keys"; - private static Path rootFolder; + private static ClientPaths clientPaths; private final ClientWindow clientWindow; private final ClientBundleGenerator clientBundleGenerator; private final boolean isEncryptionEnabled = true; - private final Path serverKeyPath; private ClientSecurity client = null; private int counter = 0; - public BundleSecurity(Path rootFolder) throws IOException, InvalidKeyException, WindowExceptions.BufferOverflow, + public BundleSecurity(ClientPaths clientPaths) throws IOException, InvalidKeyException, WindowExceptions.BufferOverflow, NoSuchAlgorithmException { - BundleSecurity.rootFolder = rootFolder; - - Path bundleSecurityPath = rootFolder.resolve(BUNDLE_SECURITY_DIR); - - serverKeyPath = bundleSecurityPath.resolve(SERVER_KEYS_SUBDIR); - - Path bundleIdNextCounter = rootFolder.resolve(BUNDLE_ID_NEXT_COUNTER); - FileUtils.createFileWithDefaultIfNeeded(bundleIdNextCounter, "0".getBytes()); + this.clientPaths = clientPaths; - Path largestBundleIdReceived = rootFolder.resolve(LARGEST_BUNDLE_ID_RECEIVED); - FileUtils.createFileWithDefaultIfNeeded(largestBundleIdReceived, "0".getBytes()); - - this.counter = Integer.valueOf(Files.readAllLines(bundleIdNextCounter).get(0)); + this.counter = Integer.valueOf(Files.readAllLines(clientPaths.bundleIdNextCounter).get(0)); /* Initializing Security Module*/ - - client = ClientSecurity.initializeInstance(1, bundleSecurityPath, serverKeyPath); - clientBundleGenerator = ClientBundleGenerator.initializeInstance(client, rootFolder); - clientWindow = ClientWindow.initializeInstance(5, client.getClientID(), rootFolder); + client = ClientSecurity.initializeInstance(1, clientPaths); + clientBundleGenerator = ClientBundleGenerator.initializeInstance(client, clientPaths); + clientWindow = ClientWindow.initializeInstance(5, client.getClientID(), clientPaths); } // TODO: this function makes me sad! it should not be static. We should probably inject BundleSecurity // into Bundle transport so that everything can be set up properly public static void initializeKeyPaths(InputStream inServerIdentity, InputStream inServerSignedPre, InputStream inServerRatchet, Path rootFolder) throws IOException { - var bundleSecurityPath = rootFolder.resolve(BUNDLE_SECURITY_DIR); - var serverKeyPath = bundleSecurityPath.resolve(SERVER_KEYS_SUBDIR); - serverKeyPath.toFile().mkdirs(); - - Path outServerIdentity = serverKeyPath.resolve(SERVER_IDENTITY_PUB); - Path outServerSignedPre = serverKeyPath.resolve(SERVER_SIGNED_PRE_PUB); - Path outServerRatchet = serverKeyPath.resolve(SERVER_RATCHET_PUB); - - Files.copy(inServerIdentity, outServerIdentity, StandardCopyOption.REPLACE_EXISTING); + Files.copy(inServerIdentity, clientPaths.outServerIdentity, StandardCopyOption.REPLACE_EXISTING); inServerIdentity.close(); - Files.copy(inServerSignedPre, outServerSignedPre, StandardCopyOption.REPLACE_EXISTING); + Files.copy(inServerSignedPre, clientPaths.outServerSignedPre, StandardCopyOption.REPLACE_EXISTING); inServerSignedPre.close(); - Files.copy(inServerRatchet, outServerRatchet, StandardCopyOption.REPLACE_EXISTING); + Files.copy(inServerRatchet, clientPaths.outServerRatchet, StandardCopyOption.REPLACE_EXISTING); inServerRatchet.close(); } diff --git a/bundle-core/src/main/java/net/discdd/client/bundlesecurity/ClientSecurity.java b/bundle-core/src/main/java/net/discdd/client/bundlesecurity/ClientSecurity.java index 9555ecbb94..dd66dca026 100644 --- a/bundle-core/src/main/java/net/discdd/client/bundlesecurity/ClientSecurity.java +++ b/bundle-core/src/main/java/net/discdd/client/bundlesecurity/ClientSecurity.java @@ -1,6 +1,7 @@ package net.discdd.client.bundlesecurity; import net.discdd.bundlesecurity.SecurityUtils; +import net.discdd.pathutils.ClientPaths; import org.whispersystems.libsignal.DuplicateMessageException; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKeyPair; @@ -60,17 +61,17 @@ public class ClientSecurity { private SignalProtocolStore clientProtocolStore; private String clientID; - private Path clientRootPath; + private ClientPaths clientPaths; - private ClientSecurity(int deviceID, Path clientRootPath, Path serverKeyPath) throws InvalidKeyException, + private ClientSecurity(int deviceID, ClientPaths clientPaths) throws InvalidKeyException, IOException, NoSuchAlgorithmException { - var clientKeyPath = clientRootPath.resolve(SecurityUtils.CLIENT_KEY_PATH); + this.clientPaths = clientPaths; // Read Server Keys from specified directory - InitializeServerKeysFromFiles(serverKeyPath); + InitializeServerKeysFromFiles(clientPaths.serverKeyPath); try { - loadKeysfromFiles(clientKeyPath); + loadKeysfromFiles(clientPaths.clientKeyPath); logger.log(FINE, "[Sec]: Using Existing Keys"); } catch (IOException | InvalidKeyException e) { logger.log(WARNING, "[Sec]: Error Loading Keys from files, generating new keys instead"); @@ -81,7 +82,7 @@ private ClientSecurity(int deviceID, Path clientRootPath, Path serverKeyPath) th ourBaseKey = Curve.generateKeyPair(); // Write generated keys to files - writeKeysToFiles(clientKeyPath, true); + writeKeysToFiles(clientPaths.clientKeyPath, true); } clientProtocolStore = SecurityUtils.createInMemorySignalProtocolStore(); @@ -90,7 +91,6 @@ private ClientSecurity(int deviceID, Path clientRootPath, Path serverKeyPath) th clientID = SecurityUtils.generateID(ourIdentityKeyPair.getPublicKey().getPublicKey().serialize()); ourAddress = new SignalProtocolAddress(clientID, deviceID); theirOneTimePreKey = Optional.absent(); - this.clientRootPath = clientRootPath; // Create Client Cipher createCipher(); @@ -156,26 +156,23 @@ private void initializeRatchet(SessionState clientSessionState) throws InvalidKe } private void updateSessionRecord() { - String sessionStorePath = clientRootPath.resolve(SecurityUtils.SESSION_STORE_FILE).toString(); - - try (FileOutputStream stream = new FileOutputStream(sessionStorePath)) { + try (FileOutputStream stream = new FileOutputStream(clientPaths.sessionStorePath.toString())) { SessionRecord clientSessionRecord = clientProtocolStore.loadSession(ourAddress); stream.write(clientSessionRecord.serialize()); } catch (IOException e) { - logger.log(SEVERE, "Error Writing Session record to " + sessionStorePath, e); + logger.log(SEVERE, "Error Writing Session record to " + clientPaths.sessionStorePath, e); } } private void createCipher() throws InvalidKeyException { - var sessionStorePath = clientRootPath.resolve(SecurityUtils.SESSION_STORE_FILE); SessionRecord clientSessionRecord = null; try { - byte[] sessionStoreBytes = Files.readAllBytes(sessionStorePath); + byte[] sessionStoreBytes = Files.readAllBytes(clientPaths.sessionStorePath); clientSessionRecord = new SessionRecord(sessionStoreBytes); } catch (IOException e) { logger.log(WARNING, - "Error Reading Session record from " + sessionStorePath + "\nCreating New Session Record!"); + "Error Reading Session record from " + clientPaths.sessionStorePath + "\nCreating New Session Record!"); clientSessionRecord = new SessionRecord(); initializeRatchet(clientSessionRecord.getSessionState()); } @@ -201,11 +198,10 @@ public String encryptBundleID(String bundleID) throws GeneralSecurityException, /* Add Headers (Identity, Base Key & Bundle ID) to Bundle Path */ /* Initialize or get previous client Security Instance */ - public static synchronized ClientSecurity initializeInstance(int deviceID, Path clientRootPath, - Path serverKeyPath) throws IOException, + public static synchronized ClientSecurity initializeInstance(int deviceID, ClientPaths clientPaths) throws IOException, NoSuchAlgorithmException, InvalidKeyException { if (singleClientInstance == null) { - singleClientInstance = new ClientSecurity(deviceID, clientRootPath, serverKeyPath); + singleClientInstance = new ClientSecurity(deviceID, clientPaths); } else { logger.log(FINE, "[Sec]: Client Security Instance is already initialized!"); } @@ -278,7 +274,7 @@ public String getBundleIDFromFile(Path bundlePath) throws IOException { } public Path getClientRootPath() { - return clientRootPath; + return clientPaths.bundleSecurityPath; } public ECPublicKey getServerPublicKey() { diff --git a/bundle-core/src/main/java/net/discdd/client/bundletransmission/BundleTransmission.java b/bundle-core/src/main/java/net/discdd/client/bundletransmission/BundleTransmission.java index 626917ae32..e94619186d 100644 --- a/bundle-core/src/main/java/net/discdd/client/bundletransmission/BundleTransmission.java +++ b/bundle-core/src/main/java/net/discdd/client/bundletransmission/BundleTransmission.java @@ -34,9 +34,9 @@ import net.discdd.model.Payload; import net.discdd.model.UncompressedBundle; import net.discdd.model.UncompressedPayload; +import net.discdd.pathutils.ClientPaths; import net.discdd.utils.AckRecordUtils; import net.discdd.utils.BundleUtils; -import net.discdd.utils.Constants; import net.discdd.utils.FileUtils; import org.whispersystems.libsignal.DuplicateMessageException; import org.whispersystems.libsignal.InvalidKeyException; @@ -57,7 +57,6 @@ import java.net.Socket; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; @@ -76,54 +75,25 @@ public class BundleTransmission { private static final Logger logger = Logger.getLogger(BundleTransmission.class.getName()); - /* Bundle generation directory */ - private static final String BUNDLE_GENERATION_DIRECTORY = "BundleTransmission/bundle-generation"; - private static final String TO_BE_BUNDLED_DIRECTORY = "to-be-bundled"; - private static final String TO_SEND_DIRECTORY = "to-send"; - private static final String UNCOMPRESSED_PAYLOAD = "uncompressed-payload"; - private static final String COMPRESSED_PAYLOAD = "compressed-payload"; - private static final String ENCRYPTED_PAYLOAD = "encrypted-payload"; - private static final String RECEIVED_PROCESSING = "received-processing"; - private static final String LARGEST_BUNDLE_ID_RECEIVED = "Shared/DB/LARGEST_BUNDLE_ID_RECEIVED.txt"; - private static final String RECEIVED_BUNDLES_DIRECTORY = "Shared/received-bundles"; + private final BundleSecurity bundleSecurity; private final ApplicationDataManager applicationDataManager; - private final long BUNDLE_SIZE_LIMIT = 100_000_000L; - - final private Path ROOT_DIR; - private final Path ackRecordPath; - private final Path tosendDir; private ClientRouting clientRouting; + private ClientPaths clientPaths; public BundleTransmission(Path rootFolder, Consumer aduConsumer) throws WindowExceptions.BufferOverflow, IOException, InvalidKeyException, RoutingExceptions.ClientMetaDataFileException, NoSuchAlgorithmException { - this.ROOT_DIR = rootFolder; - this.bundleSecurity = new BundleSecurity(this.ROOT_DIR); - this.applicationDataManager = new ApplicationDataManager(this.ROOT_DIR, aduConsumer); - - this.clientRouting = ClientRouting.initializeInstance(rootFolder); - - var bundleGenerationDir = ROOT_DIR.resolve(BUNDLE_GENERATION_DIRECTORY); - var toBeBundledDir = ROOT_DIR.resolve(TO_BE_BUNDLED_DIRECTORY); - ackRecordPath = toBeBundledDir.resolve(Constants.BUNDLE_ACKNOWLEDGEMENT_FILE_NAME); - net.discdd.utils.FileUtils.createFileWithDefaultIfNeeded(ackRecordPath, "HB".getBytes()); - tosendDir = bundleGenerationDir.resolve(TO_SEND_DIRECTORY); - tosendDir.toFile().mkdirs(); - var uncompressedPayloadDir = bundleGenerationDir.resolve(UNCOMPRESSED_PAYLOAD); - uncompressedPayloadDir.toFile().mkdirs(); - var compressedPayloadDir = bundleGenerationDir.resolve(COMPRESSED_PAYLOAD); - compressedPayloadDir.toFile().mkdirs(); - var encryptedPayloadDir = bundleGenerationDir.resolve(ENCRYPTED_PAYLOAD); - encryptedPayloadDir.toFile().mkdirs(); - var receivedProcDir = bundleGenerationDir.resolve(RECEIVED_PROCESSING); - receivedProcDir.toFile().mkdirs(); + this.clientPaths = new ClientPaths(rootFolder); + this.bundleSecurity = new BundleSecurity(clientPaths); + this.applicationDataManager = new ApplicationDataManager(clientPaths, aduConsumer); + this.clientRouting = ClientRouting.initializeInstance(clientPaths); } public void registerBundleId(String bundleId) throws IOException, WindowExceptions.BufferOverflow, GeneralSecurityException, InvalidKeyException { try (BufferedWriter bufferedWriter = new BufferedWriter( - new FileWriter(this.ROOT_DIR.resolve(LARGEST_BUNDLE_ID_RECEIVED).toFile()))) { + new FileWriter(clientPaths.largestBundleIdReceived.toFile()))) { bufferedWriter.write(bundleId); } @@ -134,7 +104,7 @@ public void registerBundleId(String bundleId) throws IOException, WindowExceptio private String getLargestBundleIdReceived() throws IOException { String bundleId = ""; try (BufferedReader bufferedReader = new BufferedReader( - new FileReader(this.ROOT_DIR.resolve(LARGEST_BUNDLE_ID_RECEIVED).toFile()))) { + new FileReader(clientPaths.largestBundleIdReceived.toFile()))) { String line; while ((line = bufferedReader.readLine()) != null) { bundleId = line.trim(); @@ -149,8 +119,7 @@ public void processReceivedBundle(BundleSender sender, Bundle bundle) throws IOE DuplicateMessageException, LegacyMessageException, InvalidKeyException, GeneralSecurityException, WindowExceptions.BufferOverflow { String largestBundleIdReceived = this.getLargestBundleIdReceived(); - UncompressedBundle uncompressedBundle = BundleUtils.extractBundle(bundle, this.ROOT_DIR.resolve( - Paths.get(BUNDLE_GENERATION_DIRECTORY, RECEIVED_PROCESSING))); + UncompressedBundle uncompressedBundle = BundleUtils.extractBundle(bundle, clientPaths.uncompressedPayloadStore); Payload payload = this.bundleSecurity.decryptPayload(uncompressedBundle); logger.log(INFO, "Updating client routing metadata for sender: " + bundleSenderToString(sender)); clientRouting.updateMetaData(sender.getId()); @@ -169,7 +138,7 @@ public void processReceivedBundle(BundleSender sender, Bundle bundle) throws IOE UncompressedPayload uncompressedPayload = BundleUtils.extractPayload(payload, uncompressedBundle.getSource().toPath()); - AckRecordUtils.writeAckRecordToFile(new Acknowledgement(bundleId), ackRecordPath); + AckRecordUtils.writeAckRecordToFile(new Acknowledgement(bundleId), clientPaths.ackRecordPath); this.registerBundleId(bundleId); String ackedBundleId = uncompressedPayload.getAckRecord().getBundleId(); @@ -184,8 +153,8 @@ public static String bundleSenderToString(BundleSender sender) { private BundleDTO generateNewBundle(String bundleId) throws RoutingExceptions.ClientMetaDataFileException, IOException, NoSuchAlgorithmException, InvalidKeyException { - Acknowledgement ackRecord = AckRecordUtils.readAckRecordFromFile(ackRecordPath); - List adus = this.applicationDataManager.fetchADUsToSend(BUNDLE_SIZE_LIMIT, null); + Acknowledgement ackRecord = AckRecordUtils.readAckRecordFromFile(clientPaths.ackRecordPath); + List adus = this.applicationDataManager.fetchADUsToSend(clientPaths.BUNDLE_SIZE_LIMIT, null); var routingData = clientRouting.bundleMetaData(); var baos = new ByteArrayOutputStream(); @@ -193,7 +162,7 @@ private BundleDTO generateNewBundle(String bundleId) throws RoutingExceptions.Cl BundleUtils.createBundlePayloadForAdus(adus, routingData, ackedEncryptedBundleId, baos); ClientSecurity clientSecurity = bundleSecurity.getClientSecurity(); - Path bundleFile = tosendDir.resolve(bundleId); + Path bundleFile = clientPaths.tosendDir.resolve(bundleId); try (OutputStream os = Files.newOutputStream(bundleFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { BundleUtils.encryptPayloadAndCreateBundle(bytes -> clientSecurity.encrypt(bytes), @@ -208,7 +177,7 @@ private BundleDTO generateNewBundle(String bundleId) throws RoutingExceptions.Cl public BundleDTO generateBundleForTransmission() throws RoutingExceptions.ClientMetaDataFileException, IOException, InvalidKeyException, GeneralSecurityException { // find the latest sent bundle - var sentBundles = tosendDir.toFile().listFiles(); + var sentBundles = clientPaths.tosendDir.toFile().listFiles(); if (sentBundles != null && sentBundles.length > 0) { // sort in reverse order of last modified time so the newest bundle is first Arrays.sort(sentBundles, (a, b) -> Long.compare(b.lastModified(), a.lastModified())); @@ -217,7 +186,7 @@ public BundleDTO generateBundleForTransmission() throws RoutingExceptions.Client var lastBundleSentTimestamp = lastSentBundle.lastModified(); // lets check to see if we have gotten new ADUs or a new ack record - if (ackRecordPath.toFile().lastModified() <= lastBundleSentTimestamp && + if (clientPaths.ackRecordPath.toFile().lastModified() <= lastBundleSentTimestamp && !applicationDataManager.hasNewADUs(null, lastBundleSentTimestamp)) { return new BundleDTO(lastSentBundle.getName(), new Bundle(lastSentBundle)); } @@ -450,12 +419,7 @@ private Path downloadBundles(List bundleRequests, BundleSender sender, stub.withDeadlineAfter(GRPC_LONG_TIMEOUT_MS, TimeUnit.MILLISECONDS).downloadBundle(downloadRequest); OutputStream fileOutputStream = null; - var receiveBundlePath = ROOT_DIR.resolve(RECEIVED_BUNDLES_DIRECTORY); - if (!Files.exists(receiveBundlePath)) { - Files.createDirectories(receiveBundlePath); - } - - var bundlePath = receiveBundlePath.resolve(bundle); + var bundlePath = clientPaths.receiveBundlePath.resolve(bundle); try { fileOutputStream = Files.newOutputStream(bundlePath, StandardOpenOption.CREATE, @@ -485,3 +449,4 @@ private Path downloadBundles(List bundleRequests, BundleSender sender, return null; } } + diff --git a/bundle-core/src/main/java/net/discdd/pathutils/ClientPaths.java b/bundle-core/src/main/java/net/discdd/pathutils/ClientPaths.java new file mode 100644 index 0000000000..2de3f56a23 --- /dev/null +++ b/bundle-core/src/main/java/net/discdd/pathutils/ClientPaths.java @@ -0,0 +1,142 @@ +package net.discdd.pathutils; + +import net.discdd.bundlesecurity.SecurityUtils; +import net.discdd.utils.Constants; +import net.discdd.utils.FileUtils; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ClientPaths { + // client security + public final Path sessionStorePath; + public final Path clientKeyPath; + // client window + public static final String CLIENT_WINDOW_SUBDIR = "ClientWindow"; + private final String WINDOW_FILE = "clientWindow.csv"; + public final Path clientWindowDataPath; + public final Path dbFile; + + // client bundle generator + public final Path counterFilePath; + public final java.io.File metadataFile; + + // client routing + private final String METADATAFILE = "routing.metadata"; + + // BundleSecurity directory + public static final String BUNDLE_SECURITY_DIR = "BundleSecurity"; + private static final String BUNDLE_ID_NEXT_COUNTER = "Shared/DB/BUNDLE_ID_NEXT_COUNTER.txt"; + public static final String SERVER_IDENTITY_PUB = "server_identity.pub"; + public static final String SERVER_SIGNED_PRE_PUB = "server_signed_pre.pub"; + public static final String SERVER_RATCHET_PUB = "server_ratchet.pub"; + public static final String SERVER_KEYS_SUBDIR = "Server_Keys"; + + /* Bundle generation directory */ + private static final String BUNDLE_GENERATION_DIRECTORY = "BundleTransmission/bundle-generation"; + private static final String TO_BE_BUNDLED_DIRECTORY = "to-be-bundled"; + private static final String TO_SEND_DIRECTORY = "to-send"; + private static final String UNCOMPRESSED_PAYLOAD = "uncompressed-payload"; + private static final String COMPRESSED_PAYLOAD = "compressed-payload"; + private static final String ENCRYPTED_PAYLOAD = "encrypted-payload"; + private static final String RECEIVED_PROCESSING = "received-processing"; + private static final String LARGEST_BUNDLE_ID_RECEIVED = "Shared/DB/LARGEST_BUNDLE_ID_RECEIVED.txt"; + private static final String RECEIVED_BUNDLES_DIRECTORY = "Shared/received-bundles"; + + private static String SENT_BUNDLE_DETAILS = "Shared/DB/SENT_BUNDLE_DETAILS.json"; + + private static String LAST_SENT_BUNDLE_STRUCTURE = "Shared/DB/LAST_SENT_BUNDLE_STRUCTURE.json"; + + public Long APP_DATA_SIZE_LIMIT = 1000000000L; + public final long BUNDLE_SIZE_LIMIT = 100_000_000L; + + public final Path ackRecordPath; + public final Path tosendDir; + + public final Path bundleGenerationDir; + public final Path toBeBundledDir; + public final Path uncompressedPayloadDir; + public final Path compressedPayloadDir; + public final Path encryptedPayloadDir; + public final Path receivedProcDir; + public final Path uncompressedPayloadStore; + public final Path largestBundleIdReceived; + public final Path receiveBundlePath; + public final Path sendADUsPath; + public final Path receiveADUsPath; + public final Path sendBundleDetailsPath; + public final Path lastSentBundleStructurePath; + + // Bundle security directory + public final Path bundleSecurityPath; + public final Path serverKeyPath; + public final Path bundleIdNextCounter; + + // initialize key paths + public final Path outServerIdentity; + public final Path outServerSignedPre; + public final Path outServerRatchet; + public ClientPaths(Path rootDir) throws IOException { + // Bundle generation directory + bundleGenerationDir = rootDir.resolve(BUNDLE_GENERATION_DIRECTORY); + toBeBundledDir = rootDir.resolve(TO_BE_BUNDLED_DIRECTORY); + ackRecordPath = toBeBundledDir.resolve(Constants.BUNDLE_ACKNOWLEDGEMENT_FILE_NAME); + net.discdd.utils.FileUtils.createFileWithDefaultIfNeeded(ackRecordPath, "HB".getBytes()); + tosendDir = bundleGenerationDir.resolve(TO_SEND_DIRECTORY); + tosendDir.toFile().mkdirs(); + + uncompressedPayloadDir = bundleGenerationDir.resolve(UNCOMPRESSED_PAYLOAD); + uncompressedPayloadDir.toFile().mkdirs(); + + compressedPayloadDir = bundleGenerationDir.resolve(COMPRESSED_PAYLOAD); + compressedPayloadDir.toFile().mkdirs(); + + encryptedPayloadDir = bundleGenerationDir.resolve(ENCRYPTED_PAYLOAD); + encryptedPayloadDir.toFile().mkdirs(); + + receivedProcDir = bundleGenerationDir.resolve(RECEIVED_PROCESSING); + receivedProcDir.toFile().mkdirs(); + + uncompressedPayloadStore = rootDir.resolve(Paths.get(BUNDLE_GENERATION_DIRECTORY, RECEIVED_PROCESSING)); + largestBundleIdReceived = rootDir.resolve(LARGEST_BUNDLE_ID_RECEIVED); + receiveBundlePath = rootDir.resolve(RECEIVED_BUNDLES_DIRECTORY); + receiveBundlePath.toFile().mkdirs(); + + // Application Data Manager + sendADUsPath = rootDir.resolve("send"); + receiveADUsPath = rootDir.resolve("receive"); + sendBundleDetailsPath = rootDir.resolve(SENT_BUNDLE_DETAILS); + lastSentBundleStructurePath = rootDir.resolve(LAST_SENT_BUNDLE_STRUCTURE); + + // Bundle security directory + bundleSecurityPath = rootDir.resolve(BUNDLE_SECURITY_DIR); + serverKeyPath = bundleSecurityPath.resolve(SERVER_KEYS_SUBDIR); + serverKeyPath.toFile().mkdirs(); + + bundleIdNextCounter = rootDir.resolve(BUNDLE_ID_NEXT_COUNTER); + FileUtils.createFileWithDefaultIfNeeded(bundleIdNextCounter, "0".getBytes()); + FileUtils.createFileWithDefaultIfNeeded(largestBundleIdReceived, "0".getBytes()); + + // initialize key paths + outServerIdentity = serverKeyPath.resolve(SERVER_IDENTITY_PUB); + outServerSignedPre = serverKeyPath.resolve(SERVER_SIGNED_PRE_PUB); + outServerRatchet = serverKeyPath.resolve(SERVER_RATCHET_PUB); + + // client routing + var metaDataPath = rootDir.resolve("BundleRouting"); + metaDataPath.toFile().mkdirs(); + metadataFile = metaDataPath.resolve(METADATAFILE).toFile(); + + // client bundle generator + counterFilePath = rootDir.resolve(Paths.get("BundleRouting", "sentBundle.id")); + + // client window + clientWindowDataPath = rootDir.resolve(CLIENT_WINDOW_SUBDIR); + clientWindowDataPath.toFile().mkdirs(); + dbFile = clientWindowDataPath.resolve(WINDOW_FILE); + + // client security + clientKeyPath = bundleSecurityPath.resolve(SecurityUtils.CLIENT_KEY_PATH); + sessionStorePath = bundleSecurityPath.resolve(SecurityUtils.SESSION_STORE_FILE); + } +} diff --git a/bundle-core/src/main/java/net/discdd/pathutils/TransportPaths.java b/bundle-core/src/main/java/net/discdd/pathutils/TransportPaths.java index bc7aeacd9b..e30fa5eb54 100644 --- a/bundle-core/src/main/java/net/discdd/pathutils/TransportPaths.java +++ b/bundle-core/src/main/java/net/discdd/pathutils/TransportPaths.java @@ -27,7 +27,7 @@ public TransportPaths(Path rootDir) { this.toServerPath = rootDir.resolve("BundleTransmission/server"); try { - if (!Files.exists(toClientPath) || !Files.isDirectory(toServerPath)) { + if (!Files.exists(toClientPath) || !Files.exists(toServerPath)) { Files.createDirectories(toClientPath); Files.createDirectories(toServerPath); } diff --git a/bundleserver/src/test/java/net/discdd/server/BundleClientToBundleServerTest.java b/bundleserver/src/test/java/net/discdd/server/BundleClientToBundleServerTest.java index c4c8f469e3..09554e89ee 100644 --- a/bundleserver/src/test/java/net/discdd/server/BundleClientToBundleServerTest.java +++ b/bundleserver/src/test/java/net/discdd/server/BundleClientToBundleServerTest.java @@ -7,7 +7,6 @@ import io.grpc.stub.StreamObserver; import net.discdd.bundlerouting.RoutingExceptions; import net.discdd.bundlerouting.service.BundleUploadResponseObserver; -import net.discdd.client.bundlesecurity.BundleSecurity; import net.discdd.client.bundletransmission.BundleTransmission; import net.discdd.grpc.AppDataUnit; import net.discdd.grpc.BundleChunk; @@ -22,6 +21,7 @@ import net.discdd.grpc.Status; import net.discdd.model.Bundle; import net.discdd.model.BundleDTO; +import net.discdd.pathutils.ClientPaths; import net.discdd.utils.Constants; import net.discdd.utils.StoreADUs; import org.junit.jupiter.api.Assertions; @@ -65,11 +65,11 @@ public class BundleClientToBundleServerTest extends End2EndTest { @BeforeAll static void setUp() throws Exception { var securityDir = - clientTestRoot.resolve(Path.of(BundleSecurity.BUNDLE_SECURITY_DIR, BundleSecurity.SERVER_KEYS_SUBDIR)); + clientTestRoot.resolve(Path.of(ClientPaths.BUNDLE_SECURITY_DIR, ClientPaths.SERVER_KEYS_SUBDIR)); securityDir.toFile().mkdirs(); - Files.copy(serverIdentityKeyPath, securityDir.resolve(BundleSecurity.SERVER_IDENTITY_PUB)); - Files.copy(serverSignedPreKeyPath, securityDir.resolve(BundleSecurity.SERVER_SIGNED_PRE_PUB)); - Files.copy(serverRatchetKeyPath, securityDir.resolve(BundleSecurity.SERVER_RATCHET_PUB)); + Files.copy(serverIdentityKeyPath, securityDir.resolve(ClientPaths.SERVER_IDENTITY_PUB)); + Files.copy(serverSignedPreKeyPath, securityDir.resolve(ClientPaths.SERVER_SIGNED_PRE_PUB)); + Files.copy(serverRatchetKeyPath, securityDir.resolve(ClientPaths.SERVER_RATCHET_PUB)); sendStore = new StoreADUs(clientTestRoot.resolve("send")); recieveStore = new StoreADUs(clientTestRoot.resolve("receive"));