diff --git a/src/main/java/org/cryptomator/common/RecoverUtil.java b/src/main/java/org/cryptomator/common/RecoverUtil.java new file mode 100644 index 000000000..f014e0e0d --- /dev/null +++ b/src/main/java/org/cryptomator/common/RecoverUtil.java @@ -0,0 +1,86 @@ +package org.cryptomator.common; + +import org.cryptomator.common.vaults.VaultState; +import org.cryptomator.cryptolib.api.Cryptor; +import org.cryptomator.cryptolib.api.CryptorProvider; +import org.cryptomator.cryptolib.api.Masterkey; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.security.SecureRandom; +import java.util.Optional; +import java.util.stream.Stream; + +import static org.cryptomator.common.vaults.VaultState.Value.LOCKED; +import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME; +import static org.cryptomator.cryptolib.api.CryptorProvider.Scheme.SIV_CTRMAC; +import static org.cryptomator.cryptolib.api.CryptorProvider.Scheme.SIV_GCM; + +public class RecoverUtil { + + public static CryptorProvider.Scheme detectCipherCombo(byte[] masterkey, Path pathToVault) { + try { + Path dDirPath = pathToVault.resolve(DATA_DIR_NAME); + + Optional firstC9rFile; + try (Stream paths = Files.walk(dDirPath)) { + firstC9rFile = paths.filter(path -> path.toString().endsWith(".c9r")).findFirst(); + } + if (firstC9rFile.isEmpty()) { + throw new IllegalStateException("No .c9r file found."); + } + + Path c9rFile = firstC9rFile.get(); + if (canDecryptFileHeader(c9rFile, new Masterkey(masterkey), SIV_GCM)) { + return SIV_GCM; + } + if (canDecryptFileHeader(c9rFile, new Masterkey(masterkey), SIV_CTRMAC)) { + return SIV_CTRMAC; + } + + return null; + } catch (IOException e) { + throw new IllegalStateException("Failed to detect cipher combo.", e); + } + } + + private static boolean canDecryptFileHeader(Path c9rFile, Masterkey masterkey, CryptorProvider.Scheme scheme) { + try (Cryptor cryptor = CryptorProvider.forScheme(scheme).provide(masterkey, SecureRandom.getInstanceStrong())) { + ByteBuffer header = ByteBuffer.wrap(Files.readAllBytes(c9rFile)); + cryptor.fileHeaderCryptor().decryptHeader(header); + return true; + } catch (Exception e) { + return false; + } + } + + public static VaultState.Value tryBackUpConfig(Path pathToConfig, VaultState.Value vaultState) { + try (Stream files = Files.list(pathToConfig.getParent())) { + Path backupFile = files.filter(file -> { + String fileName = file.getFileName().toString(); + return switch (vaultState) { + case VAULT_CONFIG_MISSING -> fileName.startsWith("vault.cryptomator") && fileName.endsWith(".bkup"); + case MASTERKEY_MISSING -> fileName.startsWith("masterkey.cryptomator") && fileName.endsWith(".bkup"); + default -> false; + }; + }).findFirst().orElse(null); + + if (backupFile != null) { + try { + Files.copy(backupFile, pathToConfig, StandardCopyOption.REPLACE_EXISTING); + return LOCKED; + } catch (IOException e) { + return vaultState; + } + } else { + return vaultState; + } + } catch (IOException e) { + return vaultState; + } + } + +} diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 32caa3205..420b60c79 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -9,13 +9,14 @@ package org.cryptomator.common.vaults; import org.apache.commons.lang3.SystemUtils; +import org.cryptomator.common.RecoverUtil; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.DirStructure; import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.integrations.mount.MountService; -import org.cryptomator.ui.keyloading.hub.HubKeyLoadingStrategy; +import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,13 +27,11 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.ResourceBundle; -import java.util.stream.Stream; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME; @@ -124,11 +123,15 @@ public class VaultListManager { private Vault create(VaultSettings vaultSettings) { var wrapper = new VaultConfigCache(vaultSettings); try { - if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) { - var keyIdScheme = wrapper.get().getKeyId().getScheme(); - vaultSettings.lastKnownKeyLoader.set(keyIdScheme); + try { + if (Objects.isNull(vaultSettings.lastKnownKeyLoader.get())) { + var keyIdScheme = wrapper.get().getKeyId().getScheme(); + vaultSettings.lastKnownKeyLoader.set(keyIdScheme); + } + } catch (NoSuchFileException e) { + LOG.warn("Vault config file not found."); } - var vaultState = determineVaultState(vaultSettings.path.get()); + var vaultState = determineVaultState(vaultSettings.path.get(),vaultSettings); if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state wrapper.reloadConfig(); } @@ -145,10 +148,10 @@ public class VaultListManager { return switch (previousState) { case LOCKED, NEEDS_MIGRATION, MISSING, VAULT_CONFIG_MISSING, MASTERKEY_MISSING -> { try { - var determinedState = determineVaultState(vault.getPath()); + var determinedState = determineVaultState(vault.getPath(),vault.getVaultSettings()); if(determinedState == MASTERKEY_MISSING){ var vaultScheme = vault.getVaultConfigCache().getUnchecked().getKeyId().getScheme(); - if((vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTP) || vaultScheme.equals(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS))){ + if(KeyLoadingStrategy.isHubVault(vaultScheme)){ determinedState = LOCKED; } } @@ -168,58 +171,24 @@ public class VaultListManager { }; } - private static VaultState.Value determineVaultState(Path pathToVault) throws IOException { + private static VaultState.Value determineVaultState(Path pathToVault, VaultSettings vaultSettings) throws IOException { Path pathToVaultConfig = Path.of(pathToVault.toString(),"vault.cryptomator"); Path pathToMasterkey = Path.of(pathToVault.toString(),"masterkey.cryptomator"); + if (!Files.exists(pathToVault)) { return VaultState.Value.MISSING; } - else if (!Files.exists(pathToVaultConfig)) { - try (Stream files = Files.list(pathToVaultConfig.getParent())) { - Path backupFile = files.filter(file -> { - String fileName = file.getFileName().toString(); - return fileName.startsWith("vault.cryptomator") && fileName.endsWith(".bkup"); - }).findFirst().orElse(null); - - if (backupFile != null) { - try { - Files.copy(backupFile, pathToVaultConfig, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - LOG.error("error",e); - return VAULT_CONFIG_MISSING; - } - } else { - return VAULT_CONFIG_MISSING; - } - } catch (IOException e) { - LOG.error("error",e); - return VAULT_CONFIG_MISSING; - } - + else if (Files.notExists(pathToVaultConfig)) { + return RecoverUtil.tryBackUpConfig(pathToVaultConfig, VAULT_CONFIG_MISSING); } - else if (!Files.exists(pathToMasterkey)) { - try (Stream files = Files.list(pathToMasterkey.getParent())) { - Path backupFile = files.filter(file -> { - String fileName = file.getFileName().toString(); - return fileName.startsWith("masterkey.cryptomator") && fileName.endsWith(".bkup"); - }).findFirst().orElse(null); - - if (backupFile != null) { - try { - Files.copy(backupFile, pathToMasterkey, StandardCopyOption.REPLACE_EXISTING); - return MASTERKEY_MISSING; - } catch (IOException e) { - LOG.error("error",e); - return MASTERKEY_MISSING; - } - } else { - return MASTERKEY_MISSING; - } - } catch (IOException e) { - LOG.error("error",e); - return MASTERKEY_MISSING; - } + else if (Files.notExists(pathToMasterkey) && + KeyLoadingStrategy.isMasterkeyFileVault(vaultSettings.lastKnownKeyLoader.get())) { + return RecoverUtil.tryBackUpConfig(pathToMasterkey, MASTERKEY_MISSING); } + return checkDirStructure(pathToVault); + } + + private static VaultState.Value checkDirStructure(Path pathToVault) throws IOException{ return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) { case VAULT -> VaultState.Value.LOCKED; case UNRELATED -> VaultState.Value.MISSING; diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java index be9ea15c7..71e525e42 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/ChooseExistingVaultController.java @@ -94,6 +94,11 @@ public class ChooseExistingVaultController implements FxController { } } + @FXML + public void restoreVaultConfigWithRecoveryKey() { + //appWindows.showErrorWindow(e, window, window.getScene()); + } + /* Getter */ public ObservableValue screenshotProperty() { diff --git a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java index 5107fe740..5ac9914b6 100644 --- a/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java +++ b/src/main/java/org/cryptomator/ui/dialogs/Dialogs.java @@ -47,6 +47,16 @@ public class Dialogs { }); } + public SimpleDialog.Builder prepareContactHubAdmin(Stage window) { + return createDialogBuilder().setOwner(window) // + .setTitleKey("contactHubAdmin.title") // + .setMessageKey("contactHubAdmin.message") // + .setDescriptionKey("contactHubAdmin.description") // + .setIcon(FontAwesome5Icon.EXCLAMATION)// + .setOkButtonKey("removeVault.confirmBtn") // + .setCancelButtonKey("generic.button.cancel"); + } + public SimpleDialog.Builder prepareRemoveCertDialog(Stage window, Settings settings) { return createDialogBuilder() // .setOwner(window) // diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailMissingVaultController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailMissingVaultController.java index 3815317a9..fc52aa775 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailMissingVaultController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailMissingVaultController.java @@ -4,6 +4,7 @@ import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultListManager; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.dialogs.Dialogs; +import org.cryptomator.ui.keyloading.KeyLoadingStrategy; import org.cryptomator.ui.recoverykey.RecoveryKeyComponent; import javax.inject.Inject; @@ -54,7 +55,12 @@ public class VaultDetailMissingVaultController implements FxController { @FXML void restoreVaultConfig() { - recoveryKeyWindow.create(vault.get(), window).showIsHubVaultDialogWindow(); + if(KeyLoadingStrategy.isHubVault(vault.get().getVaultSettings().lastKnownKeyLoader.get())){ + dialogs.prepareContactHubAdmin(window).build().showAndWait(); + } + else { + recoveryKeyWindow.create(vault.get(), window).showIsHubVaultDialogWindow(); + } } @FXML diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyRecoverController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyRecoverController.java index 944c52043..b22054f56 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyRecoverController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyRecoverController.java @@ -1,9 +1,6 @@ package org.cryptomator.ui.recoverykey; import dagger.Lazy; -import org.cryptomator.common.Nullable; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; @@ -11,9 +8,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; -import javafx.beans.Observable; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.scene.Scene; import javafx.stage.Stage; @@ -31,7 +25,9 @@ public class RecoveryKeyRecoverController implements FxController { RecoveryKeyValidateController recoveryKeyValidateController; @Inject - public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy resetPasswordScene, ResourceBundle resourceBundle) { + public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, // + @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy resetPasswordScene, // + ResourceBundle resourceBundle) { this.window = window; window.setTitle(resourceBundle.getString("recoveryKey.recover.title")); this.resetPasswordScene = resetPasswordScene; diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java index 46867b356..04439c1eb 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.recoverykey; import dagger.Lazy; +import org.cryptomator.common.RecoverUtil; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.cryptofs.CryptoFileSystemProperties; @@ -95,9 +96,10 @@ public class RecoveryKeyResetPasswordController implements FxController { Path masterkeyFilePath = recoveryPath.resolve(MASTERKEY_FILENAME); try (Masterkey masterkey = masterkeyFileAccess.load(masterkeyFilePath, newPasswordController.passwordField.getCharacters())) { try { + var combo = RecoverUtil.detectCipherCombo(masterkey.getEncoded(),vaultPath); MasterkeyLoader loader = ignored -> masterkey.copy(); CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() // - .withCipherCombo(CryptorProvider.Scheme.SIV_CTRMAC) // + .withCipherCombo(combo) // .withKeyLoader(loader) // .withShorteningThreshold(220) // .build(); diff --git a/src/main/resources/fxml/addvault_existing.fxml b/src/main/resources/fxml/addvault_existing.fxml index 200eae4f9..c4a015a6f 100644 --- a/src/main/resources/fxml/addvault_existing.fxml +++ b/src/main/resources/fxml/addvault_existing.fxml @@ -11,7 +11,7 @@ xmlns="http://javafx.com/javafx" fx:controller="org.cryptomator.ui.addvaultwizard.ChooseExistingVaultController" prefWidth="450" - prefHeight="450" + prefHeight="480" spacing="24" alignment="CENTER"> @@ -20,13 +20,14 @@ -