diff --git a/main/pom.xml b/main/pom.xml
index 3eee68f51..15fa2d689 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -24,8 +24,7 @@
UTF-8
- 1.2.2
- 1.9.0-beta1
+ 1.9.0-beta2
2.2.1
1.2.0
1.1.11
@@ -82,11 +81,6 @@
-
- org.cryptomator
- cryptolib
- ${cryptomator.cryptolib.version}
-
org.cryptomator
cryptofs
diff --git a/main/ui/pom.xml b/main/ui/pom.xml
index fd0cc91fd..5e5b46c41 100644
--- a/main/ui/pom.xml
+++ b/main/ui/pom.xml
@@ -22,10 +22,6 @@
org.cryptomator
jni
-
- org.cryptomator
- cryptolib
-
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java
index c5bfb0bd4..87a979a5c 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/common/FxmlFile.java
@@ -15,6 +15,7 @@ public enum FxmlFile {
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
PREFERENCES("/fxml/preferences.fxml"), //
QUIT("/fxml/quit.fxml"), //
+ RECOVERYKEY_CREATE("/fxml/recoverykey_create.fxml"), //
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
UNLOCK("/fxml/unlock.fxml"),
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java b/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
index b727ccc78..6cc2ac57c 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/controls/NiceSecurePasswordField.java
@@ -76,6 +76,10 @@ public class NiceSecurePasswordField extends StackPane {
return passwordField.getCharacters();
}
+ public void setPassword(CharSequence password) {
+ passwordField.setPassword(password);
+ }
+
public void setPassword(char[] password) {
passwordField.setPassword(password);
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyComponent.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyComponent.java
new file mode 100644
index 000000000..f317a140f
--- /dev/null
+++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyComponent.java
@@ -0,0 +1,48 @@
+package org.cryptomator.ui.recoverykey;
+
+import dagger.BindsInstance;
+import dagger.Lazy;
+import dagger.Subcomponent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlScene;
+
+import javax.annotation.Nullable;
+import javax.inject.Named;
+import java.util.Optional;
+
+@RecoveryKeyScoped
+@Subcomponent(modules = {RecoveryKeyModule.class})
+public interface RecoveryKeyComponent {
+
+ @RecoveryKeyWindow
+ Stage window();
+
+ @FxmlScene(FxmlFile.RECOVERYKEY_CREATE)
+ Lazy scene();
+
+ default void showRecoveryKeyCreationWindow() {
+ Stage stage = window();
+ stage.setScene(scene().get());
+ stage.sizeToScene();
+ stage.show();
+ }
+
+ @Subcomponent.Builder
+ interface Builder {
+
+ @BindsInstance
+ Builder vault(@RecoveryKeyWindow Vault vault);
+
+ @BindsInstance
+ Builder password(@Nullable CharSequence password);
+
+ @BindsInstance
+ Builder owner(@Named("keyRecoveryOwner") Stage owner);
+
+ RecoveryKeyComponent build();
+ }
+
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java
new file mode 100644
index 000000000..5b466311c
--- /dev/null
+++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyCreationController.java
@@ -0,0 +1,91 @@
+package org.cryptomator.ui.recoverykey;
+
+import javafx.beans.property.ReadOnlyStringProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.fxml.FXML;
+import javafx.stage.Stage;
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.cryptofs.CryptoFileSystemProvider;
+import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.common.Tasks;
+import org.cryptomator.ui.controls.NiceSecurePasswordField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.ExecutorService;
+
+@RecoveryKeyScoped
+public class RecoveryKeyCreationController implements FxController {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyCreationController.class);
+ private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
+
+ private final Stage window;
+ private final Vault vault;
+ private final ExecutorService executor;
+ private final CharSequence prefilledPassword;
+ private final WordEncoder wordEncoder;
+ private final StringProperty recoveryKey;
+ public NiceSecurePasswordField passwordField;
+
+ @Inject
+ public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, ExecutorService executor, @Nullable CharSequence prefilledPassword) {
+ this.window = window;
+ this.vault = vault;
+ this.executor = executor;
+ this.prefilledPassword = prefilledPassword;
+ this.wordEncoder = new WordEncoder();
+ this.recoveryKey = new SimpleStringProperty();
+ }
+
+ @FXML
+ public void initialize() {
+ if (prefilledPassword != null) {
+ passwordField.setPassword(prefilledPassword);
+ }
+ }
+
+ @FXML
+ public void createRecoveryKey() {
+ Tasks.create(() -> {
+ byte[] rawKey = CryptoFileSystemProvider.exportRawKey(vault.getPath(), MASTERKEY_FILENAME, new byte[0], passwordField.getCharacters());
+ assert rawKey.length == 64;
+ byte[] paddedKey = Arrays.copyOf(rawKey, 66);
+ // TODO add two-byte CRC
+
+ try {
+ return wordEncoder.encodePadded(paddedKey);
+ } finally {
+ Arrays.fill(rawKey, (byte) 0x00);
+ Arrays.fill(paddedKey, (byte) 0x00);
+ }
+ }).onSuccess(result -> {
+ recoveryKey.set(result);
+ }).onError(IOException.class, e -> {
+ LOG.error("Creation of recovery key failed.", e);
+ }).onError(InvalidPassphraseException.class, e -> {
+ // TODO shake animation? :D
+ }).runOnce(executor);
+ }
+
+ @FXML
+ public void close() {
+ window.close();
+ }
+
+ /* Getter/Setter */
+
+ public ReadOnlyStringProperty recoveryKeyProperty() {
+ return recoveryKey;
+ }
+
+ public String getRecoveryKey() {
+ return recoveryKey.get();
+ }
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyModule.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyModule.java
new file mode 100644
index 000000000..44fab7177
--- /dev/null
+++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyModule.java
@@ -0,0 +1,59 @@
+package org.cryptomator.ui.recoverykey;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoMap;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.stage.Modality;
+import javafx.stage.Stage;
+import org.cryptomator.ui.common.FXMLLoaderFactory;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.common.FxControllerKey;
+import org.cryptomator.ui.common.FxmlFile;
+import org.cryptomator.ui.common.FxmlScene;
+
+import javax.inject.Named;
+import javax.inject.Provider;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+@Module
+abstract class RecoveryKeyModule {
+
+ @Provides
+ @RecoveryKeyWindow
+ @RecoveryKeyScoped
+ static FXMLLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories, ResourceBundle resourceBundle) {
+ return new FXMLLoaderFactory(factories, resourceBundle);
+ }
+
+ @Provides
+ @RecoveryKeyWindow
+ @RecoveryKeyScoped
+ static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional windowIcon, @Named("keyRecoveryOwner") Stage owner) {
+ Stage stage = new Stage();
+ stage.setTitle("TODO keyRecovery.title"); // TODO localize
+ stage.setResizable(false);
+ stage.initModality(Modality.WINDOW_MODAL);
+ stage.initOwner(owner);
+ windowIcon.ifPresent(stage.getIcons()::add);
+ return stage;
+ }
+
+ @Provides
+ @FxmlScene(FxmlFile.RECOVERYKEY_CREATE)
+ @RecoveryKeyScoped
+ static Scene provideRecoveryKeyCreationScene(@RecoveryKeyWindow FXMLLoaderFactory fxmlLoaders, @RecoveryKeyWindow Stage window) {
+ return fxmlLoaders.createScene("/fxml/recoverykey_create.fxml");
+ }
+
+ // ------------------
+
+ @Binds
+ @IntoMap
+ @FxControllerKey(RecoveryKeyCreationController.class)
+ abstract FxController bindRecoveryKeyCreationController(RecoveryKeyCreationController controller);
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyScoped.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyScoped.java
new file mode 100644
index 000000000..e7b266605
--- /dev/null
+++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyScoped.java
@@ -0,0 +1,13 @@
+package org.cryptomator.ui.recoverykey;
+
+import javax.inject.Scope;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Scope
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RecoveryKeyScoped {
+
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyWindow.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyWindow.java
new file mode 100644
index 000000000..d43fb7b3a
--- /dev/null
+++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyWindow.java
@@ -0,0 +1,14 @@
+package org.cryptomator.ui.recoverykey;
+
+import javax.inject.Qualifier;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface RecoveryKeyWindow {
+
+}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/keyrecovery/WordEncoder.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/WordEncoder.java
similarity index 98%
rename from main/ui/src/main/java/org/cryptomator/ui/keyrecovery/WordEncoder.java
rename to main/ui/src/main/java/org/cryptomator/ui/recoverykey/WordEncoder.java
index d5d3dc091..2fc02c183 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/keyrecovery/WordEncoder.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/WordEncoder.java
@@ -1,4 +1,4 @@
-package org.cryptomator.ui.keyrecovery;
+package org.cryptomator.ui.recoverykey;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java
index cf8a804c6..5e68480a8 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/GeneralVaultOptionsController.java
@@ -5,6 +5,7 @@ import javafx.stage.Stage;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.changepassword.ChangePasswordComponent;
import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import javax.inject.Inject;
@@ -14,12 +15,14 @@ public class GeneralVaultOptionsController implements FxController {
private final Vault vault;
private final Stage window;
private final ChangePasswordComponent.Builder changePasswordWindow;
+ private final RecoveryKeyComponent.Builder recoveryKeyWindow;
@Inject
- GeneralVaultOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow) {
+ GeneralVaultOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow) {
this.vault = vault;
this.window = window;
this.changePasswordWindow = changePasswordWindow;
+ this.recoveryKeyWindow = recoveryKeyWindow;
}
@FXML
@@ -27,4 +30,9 @@ public class GeneralVaultOptionsController implements FxController {
changePasswordWindow.vault(vault).owner(window).build().showChangePasswordWindow();
}
+ @FXML
+ public void showRecoveryKey() {
+ recoveryKeyWindow.vault(vault).owner(window).build().showRecoveryKeyCreationWindow();
+ }
+
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
index 967bbbe1a..130eda0d3 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/VaultOptionsModule.java
@@ -20,6 +20,7 @@ import org.cryptomator.ui.common.FxControllerKey;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.mainwindow.MainWindow;
+import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
import javax.inject.Named;
import javax.inject.Provider;
@@ -27,7 +28,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
-@Module(subcomponents = {ChangePasswordComponent.class})
+@Module(subcomponents = {ChangePasswordComponent.class, RecoveryKeyComponent.class})
abstract class VaultOptionsModule {
@Provides
diff --git a/main/ui/src/main/resources/fxml/recoverykey_create.fxml b/main/ui/src/main/resources/fxml/recoverykey_create.fxml
new file mode 100644
index 000000000..e44314d1f
--- /dev/null
+++ b/main/ui/src/main/resources/fxml/recoverykey_create.fxml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/ui/src/main/resources/fxml/vault_options_general.fxml b/main/ui/src/main/resources/fxml/vault_options_general.fxml
index e45a2f209..cf72b60ed 100644
--- a/main/ui/src/main/resources/fxml/vault_options_general.fxml
+++ b/main/ui/src/main/resources/fxml/vault_options_general.fxml
@@ -12,5 +12,6 @@
+
diff --git a/main/ui/src/test/java/org/cryptomator/ui/keyrecovery/WordEncoderTest.java b/main/ui/src/test/java/org/cryptomator/ui/recoverykey/WordEncoderTest.java
similarity index 96%
rename from main/ui/src/test/java/org/cryptomator/ui/keyrecovery/WordEncoderTest.java
rename to main/ui/src/test/java/org/cryptomator/ui/recoverykey/WordEncoderTest.java
index 784c638b6..9d09c3d8b 100644
--- a/main/ui/src/test/java/org/cryptomator/ui/keyrecovery/WordEncoderTest.java
+++ b/main/ui/src/test/java/org/cryptomator/ui/recoverykey/WordEncoderTest.java
@@ -1,4 +1,4 @@
-package org.cryptomator.ui.keyrecovery;
+package org.cryptomator.ui.recoverykey;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;