Просмотр исходного кода

Added first prototyp for recovery key generation

Sebastian Stenzel 5 лет назад
Родитель
Сommit
1930090044

+ 1 - 7
main/pom.xml

@@ -24,8 +24,7 @@
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 
 		<!-- dependency versions -->
-		<cryptomator.cryptolib.version>1.2.2</cryptomator.cryptolib.version>
-		<cryptomator.cryptofs.version>1.9.0-beta1</cryptomator.cryptofs.version>
+		<cryptomator.cryptofs.version>1.9.0-beta2</cryptomator.cryptofs.version>
 		<cryptomator.jni.version>2.2.1</cryptomator.jni.version>
 		<cryptomator.fuse.version>1.2.0</cryptomator.fuse.version>
 		<cryptomator.dokany.version>1.1.11</cryptomator.dokany.version>
@@ -82,11 +81,6 @@
 			</dependency>
 
 			<!-- Cryptomator Libs -->
-			<dependency>
-				<groupId>org.cryptomator</groupId>
-				<artifactId>cryptolib</artifactId>
-				<version>${cryptomator.cryptolib.version}</version>
-			</dependency>
 			<dependency>
 				<groupId>org.cryptomator</groupId>
 				<artifactId>cryptofs</artifactId>

+ 0 - 4
main/ui/pom.xml

@@ -22,10 +22,6 @@
 			<groupId>org.cryptomator</groupId>
 			<artifactId>jni</artifactId>
 		</dependency>
-		<dependency>
-			<groupId>org.cryptomator</groupId>
-			<artifactId>cryptolib</artifactId>
-		</dependency>
 		
 		<!-- JavaFx -->
 		<dependency>

+ 1 - 0
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"), //

+ 4 - 0
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);
 	}

+ 48 - 0
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> 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();
+	}
+
+}

+ 91 - 0
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();
+	}
+}

+ 59 - 0
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<Class<? extends FxController>, Provider<FxController>> factories, ResourceBundle resourceBundle) {
+		return new FXMLLoaderFactory(factories, resourceBundle);
+	}
+
+	@Provides
+	@RecoveryKeyWindow
+	@RecoveryKeyScoped
+	static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> 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);
+}

+ 13 - 0
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 {
+
+}

+ 14 - 0
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 {
+
+}

+ 1 - 1
main/ui/src/main/java/org/cryptomator/ui/keyrecovery/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;

+ 9 - 1
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();
+	}
+
 }

+ 2 - 1
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

+ 46 - 0
main/ui/src/main/resources/fxml/recoverykey_create.fxml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ButtonBar?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.layout.StackPane?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.shape.Circle?>
+<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import org.cryptomator.ui.controls.NiceSecurePasswordField?>
+<?import javafx.scene.layout.HBox?>
+<VBox xmlns="http://javafx.com/javafx"
+	  xmlns:fx="http://javafx.com/fxml"
+	  fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyCreationController"
+	  minWidth="400"
+	  maxWidth="400"
+	  minHeight="145"
+	  spacing="12"
+	  alignment="TOP_CENTER">
+	<padding>
+		<Insets topRightBottomLeft="12"/>
+	</padding>
+	<children>
+		<StackPane alignment="CENTER">
+			<Circle styleClass="glyph-icon-primary" radius="36"/>
+			<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CHECK" glyphSize="36"/>
+		</StackPane>
+
+		<HBox>
+			<NiceSecurePasswordField fx:id="passwordField" HBox.hgrow="ALWAYS"/>
+			<Button text="TODO create recovery key" onAction="#createRecoveryKey" HBox.hgrow="NEVER"/>
+		</HBox>
+		
+		<!-- TODO use TextArea instead -->
+		<TextField editable="false" text="${controller.recoveryKey}"/>
+
+		<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
+			<ButtonBar buttonMinWidth="120" buttonOrder="+C">
+				<buttons>
+					<Button text="%generic.button.done" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close"/>
+				</buttons>
+			</ButtonBar>
+		</VBox>
+	</children>
+</VBox>

+ 1 - 0
main/ui/src/main/resources/fxml/vault_options_general.fxml

@@ -12,5 +12,6 @@
 	</padding>
 	<children>
 		<Button text="%vaultOptions.general.changePasswordBtn" onAction="#changePassword"/>
+		<Button text="TODO show recovery key" onAction="#showRecoveryKey"/>
 	</children>
 </VBox>

+ 1 - 1
main/ui/src/test/java/org/cryptomator/ui/keyrecovery/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;