improve RecoverUtil and determineVaultState

This commit is contained in:
Jan-Peter Klein
2025-02-21 23:24:34 +01:00
parent 676c507a50
commit 895614353e
2 changed files with 63 additions and 54 deletions

View File

@@ -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<Path> firstC9rFile;
try (Stream<Path> 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<Path> 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<Path> 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<Path> 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;
}
}

View File

@@ -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);
}