add RecoverUtil and some ui changes

This commit is contained in:
Jan-Peter Klein
2025-02-21 19:43:09 +01:00
parent 5f4fd92b1d
commit 676c507a50
9 changed files with 149 additions and 68 deletions

View 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;
}
}
}

View File

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

View File

@@ -94,6 +94,11 @@ public class ChooseExistingVaultController implements FxController {
}
}
@FXML
public void restoreVaultConfigWithRecoveryKey() {
//appWindows.showErrorWindow(e, window, window.getScene());
}
/* Getter */
public ObservableValue<Image> screenshotProperty() {

View File

@@ -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) //

View File

@@ -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

View File

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

View File

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

View File

@@ -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>

View File

@@ -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"