diff --git a/src/main/java/org/cryptomator/common/RecoverUtil.java b/src/main/java/org/cryptomator/common/RecoverUtil.java index f014e0e0d..8cce5fe09 100644 --- a/src/main/java/org/cryptomator/common/RecoverUtil.java +++ b/src/main/java/org/cryptomator/common/RecoverUtil.java @@ -11,10 +11,8 @@ 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; @@ -22,64 +20,61 @@ 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; + try (Stream paths = Files.walk(pathToVault.resolve(DATA_DIR_NAME))) { + return paths.filter(path -> path.toString().endsWith(".c9r")) + .findFirst() + .map(c9rFile -> determineScheme(c9rFile, masterkey)) + .orElseThrow(() -> new IllegalStateException("No .c9r file found.")); } 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())) { + private static CryptorProvider.Scheme determineScheme(Path c9rFile, byte[] masterkey) { + try { ByteBuffer header = ByteBuffer.wrap(Files.readAllBytes(c9rFile)); - cryptor.fileHeaderCryptor().decryptHeader(header); + return tryDecrypt(header, new Masterkey(masterkey), SIV_GCM) ? SIV_GCM : + tryDecrypt(header, new Masterkey(masterkey), SIV_CTRMAC) ? SIV_CTRMAC : null; + } catch (IOException e) { + return null; + } + } + + private static boolean tryDecrypt(ByteBuffer header, Masterkey masterkey, CryptorProvider.Scheme scheme) { + try (Cryptor cryptor = CryptorProvider.forScheme(scheme).provide(masterkey, SecureRandom.getInstanceStrong())) { + cryptor.fileHeaderCryptor().decryptHeader(header.duplicate()); 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; - } + public static boolean restoreBackupIfAvailable(Path configPath, VaultState.Value vaultState) { + try (Stream files = Files.list(configPath.getParent())) { + return files + .filter(file -> matchesBackupFile(file.getFileName().toString(), vaultState)) + .findFirst() + .map(backupFile -> copyBackupFile(backupFile, configPath)) + .orElse(false); } catch (IOException e) { - return vaultState; + return false; + } + } + + private static boolean matchesBackupFile(String fileName, VaultState.Value vaultState) { + 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; + }; + } + + private static boolean copyBackupFile(Path backupFile, Path configPath) { + try { + Files.copy(backupFile, configPath, StandardCopyOption.REPLACE_EXISTING); + return true; + } catch (IOException e) { + return false; } } diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 420b60c79..7f18e82c9 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -172,19 +172,33 @@ public class VaultListManager { } 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"); + Path pathToVaultConfig = pathToVault.resolve("vault.cryptomator"); + Path pathToMasterkey = pathToVault.resolve("masterkey.cryptomator"); - if (!Files.exists(pathToVault)) { + if (Files.notExists(pathToVault)) { return VaultState.Value.MISSING; } - else if (Files.notExists(pathToVaultConfig)) { - return RecoverUtil.tryBackUpConfig(pathToVaultConfig, VAULT_CONFIG_MISSING); + + boolean vaultConfigRestored = Files.notExists(pathToVaultConfig) && + RecoverUtil.restoreBackupIfAvailable(pathToVaultConfig, VaultState.Value.VAULT_CONFIG_MISSING); + + boolean masterkeyRestored = Files.notExists(pathToMasterkey) && + KeyLoadingStrategy.isMasterkeyFileVault(vaultSettings.lastKnownKeyLoader.get()) && + RecoverUtil.restoreBackupIfAvailable(pathToMasterkey, VaultState.Value.MASTERKEY_MISSING); + + if (vaultConfigRestored || masterkeyRestored) { + return LOCKED; } - else if (Files.notExists(pathToMasterkey) && + + if (Files.notExists(pathToVaultConfig)) { + return VaultState.Value.VAULT_CONFIG_MISSING; + } + + if (Files.notExists(pathToMasterkey) && KeyLoadingStrategy.isMasterkeyFileVault(vaultSettings.lastKnownKeyLoader.get())) { - return RecoverUtil.tryBackUpConfig(pathToMasterkey, MASTERKEY_MISSING); + return VaultState.Value.MASTERKEY_MISSING; } + return checkDirStructure(pathToVault); }