mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 02:01: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();
|
||||
|
||||
@@ -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">
|
||||
<padding>
|
||||
@@ -20,13 +20,14 @@
|
||||
<children>
|
||||
<ImageView VBox.vgrow="ALWAYS" fitWidth="400" preserveRatio="true" smooth="true" image="${controller.screenshot}"/>
|
||||
|
||||
<Label text="%addvaultwizard.existing.instruction" wrapText="true" labelFor="$finishButton"/>
|
||||
<Label text="%addvaultwizard.existing.instruction" wrapText="true"/>
|
||||
|
||||
<Region VBox.vgrow="ALWAYS"/>
|
||||
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+X">
|
||||
<ButtonBar buttonMinWidth="120" buttonOrder="+CX">
|
||||
<buttons>
|
||||
<Button fx:id="finishButton" text="%addvaultwizard.existing.chooseBtn" ButtonBar.buttonData="NEXT_FORWARD" onAction="#chooseFileAndNext" defaultButton="true"/>
|
||||
<Button text="%addvaultwizard.existing.restore" ButtonBar.buttonData="NEXT_FORWARD" onAction="#restoreVaultConfigWithRecoveryKey" />
|
||||
<Button text="%addvaultwizard.existing.chooseBtn" ButtonBar.buttonData="NEXT_FORWARD" onAction="#chooseFileAndNext" defaultButton="true"/>
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
|
||||
@@ -95,7 +95,8 @@ addvault.new.readme.accessLocation.3=Any files added to this volume will be encr
|
||||
addvault.new.readme.accessLocation.4=Feel free to remove this file.
|
||||
## Existing
|
||||
addvaultwizard.existing.title=Add Existing Vault
|
||||
addvaultwizard.existing.instruction=Choose the "vault.cryptomator" file of your existing vault. If only a file named "masterkey.cryptomator" exists, select that instead.
|
||||
addvaultwizard.existing.instruction=Choose the "vault.cryptomator" file of your existing vault. If only a file named "masterkey.cryptomator" exists, select that instead. If your vault.cryptomator lost you can recover it with your RecoveryKey.
|
||||
addvaultwizard.existing.restore=Restore…
|
||||
addvaultwizard.existing.chooseBtn=Choose…
|
||||
addvaultwizard.existing.filePickerTitle=Select Vault File
|
||||
addvaultwizard.existing.filePickerMimeDesc=Cryptomator Vault
|
||||
@@ -109,6 +110,11 @@ removeVault.message=Remove vault?
|
||||
removeVault.description=This will only make Cryptomator forget about this vault. You can add it again. No encrypted files will be deleted from your hard drive.
|
||||
removeVault.confirmBtn=Remove Vault
|
||||
|
||||
# Contact Hub Admin
|
||||
contactHubAdmin.title=Contact Admin
|
||||
contactHubAdmin.message=Contact Admin
|
||||
contactHubAdmin.description=You should contact your vault admin.
|
||||
|
||||
# Change Password
|
||||
changepassword.title=Change Password
|
||||
changepassword.enterOldPassword=Enter the current password for "%s"
|
||||
|
||||
Reference in New Issue
Block a user