From b00ee4740a8cb52a09ee090d23ef0f40264f9111 Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Mon, 7 Jul 2025 14:58:19 +0200 Subject: [PATCH] legacy vaults supported now --- .../common/recovery/BackupRestorer.java | 6 ++-- .../common/recovery/MasterkeyService.java | 18 ++++++---- .../common/vaults/VaultListManager.java | 33 ++++++++----------- .../ui/mainwindow/VaultListController.java | 2 +- .../RecoveryKeyResetPasswordController.java | 27 +++++++-------- .../RecoveryKeyValidateController.java | 16 ++++----- .../fxml/recoverykey_reset_password.fxml | 4 +-- 7 files changed, 50 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/cryptomator/common/recovery/BackupRestorer.java b/src/main/java/org/cryptomator/common/recovery/BackupRestorer.java index bbb1fa4c9..e5afc5112 100644 --- a/src/main/java/org/cryptomator/common/recovery/BackupRestorer.java +++ b/src/main/java/org/cryptomator/common/recovery/BackupRestorer.java @@ -18,7 +18,7 @@ public final class BackupRestorer { private BackupRestorer() {} - public static void restoreIfPresent(Path vaultPath, String filePrefix) { + public static void restoreIfBackupPresent(Path vaultPath, String filePrefix) { Path targetFile = vaultPath.resolve(filePrefix); try (Stream files = Files.list(vaultPath)) { @@ -45,7 +45,9 @@ public final class BackupRestorer { private static void copyBackupFile(Path backupFile, Path configPath) { try { Files.copy(backupFile, configPath, StandardCopyOption.REPLACE_EXISTING); + LOG.debug("Backup restored - file: '{}' path: '{}'", backupFile, configPath); } catch (IOException e) { - LOG.warn("Unable to copy backup file from '{}' to '{}'", backupFile, configPath, e); } + LOG.warn("Unable to copy backup file from '{}' to '{}'", backupFile, configPath, e); + } } } diff --git a/src/main/java/org/cryptomator/common/recovery/MasterkeyService.java b/src/main/java/org/cryptomator/common/recovery/MasterkeyService.java index 5fd74a7f0..d434b2a36 100644 --- a/src/main/java/org/cryptomator/common/recovery/MasterkeyService.java +++ b/src/main/java/org/cryptomator/common/recovery/MasterkeyService.java @@ -19,7 +19,6 @@ import java.nio.file.StandardOpenOption; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; -import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.stream.Stream; @@ -64,19 +63,22 @@ public final class MasterkeyService { public static Optional detect(Masterkey masterkey, Path vaultPath) { try (Stream paths = Files.walk(vaultPath.resolve(DATA_DIR_NAME))) { - List excludedFilenames = List.of("dirid.c9r", "dir.c9r"); - Optional c9rFile = paths.filter(p -> p.toString().endsWith(".c9r")).filter(p -> excludedFilenames.stream().noneMatch(p.toString()::endsWith)).findFirst(); + Optional c9rFile = paths // + .filter(p -> p.toString().endsWith(".c9r")) // + .filter(p -> !p.toString().equals("dir.c9r")) // + .findFirst(); if (c9rFile.isEmpty()) { LOG.info("Unable to detect Crypto scheme: No *.c9r file found in {}", vaultPath); return Optional.empty(); } - return determineScheme(c9rFile.get(), masterkey); // + return determineScheme(c9rFile.get(), masterkey); } catch (IOException e) { LOG.info("Unable to detect Crypto scheme: Failed to inspect vault", e); return Optional.empty(); } } + private static Optional determineScheme(Path c9rFile, Masterkey masterkey) { return Arrays.stream(CryptorProvider.Scheme.values()).filter(scheme -> { try (Cryptor cryptor = CryptorProvider.forScheme(scheme).provide(masterkey.copy(), SecureRandom.getInstanceStrong())) { @@ -90,9 +92,13 @@ public final class MasterkeyService { headerBuf.flip(); cryptor.fileHeaderCryptor().decryptHeader(headerBuf.duplicate()); + LOG.debug("Detected Crypto scheme: {}", scheme); return true; - } catch (IOException | CryptoException | NoSuchAlgorithmException e) { - LOG.info("Unable to detect Crypto scheme: Failed to decrypt .c9r file", e); + } catch (CryptoException e) { + LOG.debug("Could not decrypt with scheme: {}", scheme); + return false; + } catch (IOException | NoSuchAlgorithmException e) { + LOG.warn("Unable to detect Crypto scheme: Failed to decrypt .c9r file", e); return false; } }).findFirst(); diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 9a0b7977e..34ee47b45 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -23,13 +23,7 @@ import java.util.ResourceBundle; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME; -import static org.cryptomator.common.vaults.VaultState.Value.ERROR; -import static org.cryptomator.common.vaults.VaultState.Value.LOCKED; -import static org.cryptomator.common.vaults.VaultState.Value.ALL_MISSING; -import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION; -import static org.cryptomator.common.vaults.VaultState.Value.PROCESSING; -import static org.cryptomator.common.vaults.VaultState.Value.UNLOCKED; -import static org.cryptomator.common.vaults.VaultState.Value.VAULT_CONFIG_MISSING; +import static org.cryptomator.common.vaults.VaultState.Value.*; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Constants; @@ -194,31 +188,32 @@ public class VaultListManager { Path pathToMasterkey = pathToVault.resolve(MASTERKEY_FILENAME); if (!Files.exists(pathToVault)) { - return VaultState.Value.MISSING; + return MISSING; } - BackupRestorer.restoreIfPresent(pathToVaultConfig.getParent(), VAULTCONFIG_FILENAME); - - BackupRestorer.restoreIfPresent(pathToMasterkey.getParent(), MASTERKEY_FILENAME); + if(!Files.exists(pathToVaultConfig)) { + BackupRestorer.restoreIfBackupPresent(pathToVault, VAULTCONFIG_FILENAME); + } + if(!Files.exists(pathToMasterkey)){ + BackupRestorer.restoreIfBackupPresent(pathToVault, MASTERKEY_FILENAME); + } if (!Files.exists(pathToVaultConfig) && !Files.exists(pathToMasterkey)) { return ALL_MISSING; } - + var checkedDirStructureVaultState = checkDirStructure(pathToVault); if (!Files.exists(pathToVaultConfig)) { - return VAULT_CONFIG_MISSING; + return checkedDirStructureVaultState.equals(LOCKED) ? VAULT_CONFIG_MISSING : checkedDirStructureVaultState ; } - return checkDirStructure(pathToVault); + return checkedDirStructureVaultState; } 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; - case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? // - VaultState.Value.NEEDS_MIGRATION // - : VaultState.Value.MISSING; + case VAULT -> LOCKED; + case UNRELATED -> MISSING; + case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? NEEDS_MIGRATION : MISSING; }; } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java index 3d63fd6be..0e2065c62 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java @@ -270,7 +270,7 @@ public class VaultListController implements FxController { recoveryKeyWindow.create(preparedVault, mainWindow, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_VAULT_CONFIG)).showOnboardingDialogWindow(); case ALL_MISSING -> recoveryKeyWindow.create(preparedVault, mainWindow, new SimpleObjectProperty<>(RecoveryActionType.RESTORE_ALL)).showOnboardingDialogWindow(); - case LOCKED -> { + case LOCKED, NEEDS_MIGRATION -> { vaultListManager.addVault(preparedVault); dialogs.prepareRecoveryVaultAdded(mainWindow, preparedVault.getDisplayName()).setOkAction(Stage::close).build().showAndWait(); } diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java index 460372ca1..c810d5f22 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyResetPasswordController.java @@ -65,7 +65,6 @@ public class RecoveryKeyResetPasswordController implements FxController { private final Stage owner; public NewPasswordController newPasswordController; - public Button backButton; public Button nextButton; @Inject @@ -104,18 +103,8 @@ public class RecoveryKeyResetPasswordController implements FxController { @FXML public void initialize() { switch (recoverType.get()) { - case RESTORE_MASTERKEY -> { - nextButton.setText(resourceBundle.getString("recoveryKey.recover.recoverBtn")); - nextButton.setOnAction((_) -> resetPassword()); - } - case RESTORE_ALL -> { - nextButton.setText(resourceBundle.getString("recoveryKey.recover.recoverBtn")); - nextButton.setOnAction((_) -> restorePassword()); - } - case RESET_PASSWORD -> { - nextButton.setText(resourceBundle.getString("recoveryKey.recover.resetBtn")); - nextButton.setOnAction((_) -> resetPassword()); - } + case RESTORE_MASTERKEY, RESTORE_ALL -> nextButton.setText(resourceBundle.getString("recoveryKey.recover.recoverBtn")); + case RESET_PASSWORD -> nextButton.setText(resourceBundle.getString("recoveryKey.recover.resetBtn")); } } @@ -128,15 +117,21 @@ public class RecoveryKeyResetPasswordController implements FxController { } } + @FXML + public void next() { + switch (recoverType.get()) { + case RESTORE_ALL -> restorePassword(); + case RESTORE_MASTERKEY, RESET_PASSWORD -> resetPassword(); + } + } + @FXML public void restorePassword() { try (RecoveryDirectory recoveryDirectory = RecoveryDirectory.create(vault.getPath())) { Path recoveryPath = recoveryDirectory.getRecoveryPath(); MasterkeyService.recoverFromRecoveryKey(recoveryKey.get(), recoveryKeyFactory, recoveryPath, newPasswordController.passwordField.getCharacters()); - Path masterkeyFilePath = recoveryPath.resolve(MASTERKEY_FILENAME); - - try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, masterkeyFilePath, newPasswordController.passwordField.getCharacters())) { + try (Masterkey masterkey = MasterkeyService.load(masterkeyFileAccess, recoveryPath.resolve(MASTERKEY_FILENAME), newPasswordController.passwordField.getCharacters())) { CryptoFsInitializer.init(recoveryPath, masterkey, shorteningThreshold.get(), cipherCombo.get()); } diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java index 163d734ed..2e292d539 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java @@ -1,10 +1,11 @@ package org.cryptomator.ui.recoverykey; - import com.google.common.base.CharMatcher; import com.google.common.base.Strings; import org.cryptomator.common.Nullable; import org.cryptomator.common.ObservableUtil; +import org.cryptomator.common.recovery.MasterkeyService; +import org.cryptomator.common.recovery.RecoveryActionType; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfigLoadException; @@ -26,12 +27,9 @@ import javafx.scene.control.TextFormatter; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; -import org.cryptomator.common.recovery.MasterkeyService; -import org.cryptomator.common.recovery.RecoveryActionType; - public class RecoveryKeyValidateController implements FxController { - private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyCreationController.class); + private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyValidateController.class); private static final CharMatcher ALLOWED_CHARS = CharMatcher.inRange('a', 'z').or(CharMatcher.is(' ')); private final Vault vault; @@ -137,7 +135,7 @@ public class RecoveryKeyValidateController implements FxController { private void validateRecoveryKey() { switch (recoverType.get()) { case RESTORE_ALL, RESTORE_VAULT_CONFIG -> { - try{ + try { var combo = MasterkeyService.validateRecoveryKeyAndDetectCombo(recoveryKeyFactory, vault, recoveryKey.get(), masterkeyFileAccess); combo.ifPresent(cipherCombo::set); if (combo.isPresent()) { @@ -145,15 +143,13 @@ public class RecoveryKeyValidateController implements FxController { } else { recoveryKeyState.set(RecoveryKeyState.WRONG); } - } - catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { recoveryKeyState.set(RecoveryKeyState.INVALID); } } case RESTORE_MASTERKEY, RESET_PASSWORD, SHOW_KEY, CONVERT_VAULT -> { isWrongKey = false; - boolean valid = recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), - unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null); + boolean valid = recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null); if (valid) { recoveryKeyState.set(RecoveryKeyState.CORRECT); } else if (isWrongKey) { //set via side effect in checkKeyAgainstVaultConfig() diff --git a/src/main/resources/fxml/recoverykey_reset_password.fxml b/src/main/resources/fxml/recoverykey_reset_password.fxml index a6b255ed8..648e761fd 100644 --- a/src/main/resources/fxml/recoverykey_reset_password.fxml +++ b/src/main/resources/fxml/recoverykey_reset_password.fxml @@ -24,8 +24,8 @@ -