mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-22 04:31:27 +00:00
add RecoverUtil and some ui changes
This commit is contained in:
86
src/main/java/org/cryptomator/common/RecoverUtil.java
Normal file
86
src/main/java/org/cryptomator/common/RecoverUtil.java
Normal file
@@ -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<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;
|
||||
} 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<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;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return vaultState;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Path> 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<Path> 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;
|
||||
|
||||
@@ -94,6 +94,11 @@ public class ChooseExistingVaultController implements FxController {
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void restoreVaultConfigWithRecoveryKey() {
|
||||
//appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}
|
||||
|
||||
/* Getter */
|
||||
|
||||
public ObservableValue<Image> screenshotProperty() {
|
||||
|
||||
@@ -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) //
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Scene> resetPasswordScene, ResourceBundle resourceBundle) {
|
||||
public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, //
|
||||
@FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene, //
|
||||
ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
window.setTitle(resourceBundle.getString("recoveryKey.recover.title"));
|
||||
this.resetPasswordScene = resetPasswordScene;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user