mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-08-19 21:56:07 +00:00
pass a single keyloader to cryptofs
This commit is contained in:
@@ -123,7 +123,7 @@ public class Vault {
|
||||
}
|
||||
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
|
||||
.withKeyLoaders(keyLoader) //
|
||||
.withKeyLoader(keyLoader) //
|
||||
.withFlags(flags) //
|
||||
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
|
||||
.build();
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
<project.jdk.version>16</project.jdk.version>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.0.0-beta7</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptofs.version>2.0.0-beta8</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.0.0-beta2</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.0.0-beta2</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.0.0-beta2</cryptomator.integrations.mac.version>
|
||||
|
||||
+17
-15
@@ -8,13 +8,14 @@ import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||
import org.cryptomator.cryptofs.VaultCipherCombo;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileLoader;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.Tasks;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,6 +37,7 @@ import javafx.scene.control.ToggleGroup;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Files;
|
||||
@@ -53,6 +55,7 @@ import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
public class CreateNewVaultPasswordController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CreateNewVaultPasswordController.class);
|
||||
private static final URI DEFAULT_KEY_ID = URI.create(MasterkeyFileLoadingStrategy.SCHEME + ":" + MASTERKEY_FILENAME); // TODO better place?
|
||||
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> chooseLocationScene;
|
||||
@@ -174,23 +177,22 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME);
|
||||
try (Masterkey masterkey = Masterkey.generate(csprng)) {
|
||||
masterkeyFileAccess.persist(masterkey, masterkeyFilePath, passphrase);
|
||||
}
|
||||
|
||||
// 2. initialize vault:
|
||||
var context = new StaticMasterkeyFileLoaderContext(masterkeyFilePath, passphrase);
|
||||
var loader = masterkeyFileAccess.keyLoader(path, context);
|
||||
try {
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(VaultCipherCombo.SIV_CTRMAC).withKeyLoaders(loader).build();
|
||||
CryptoFileSystemProvider.initialize(path, fsProps, MasterkeyFileLoader.keyId(MASTERKEY_FILENAME));
|
||||
// 2. initialize vault:
|
||||
try {
|
||||
MasterkeyLoader loader = ignored -> masterkey;
|
||||
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(VaultCipherCombo.SIV_CTRMAC).withKeyLoader(loader).build();
|
||||
CryptoFileSystemProvider.initialize(path, fsProps, DEFAULT_KEY_ID);
|
||||
|
||||
// 3. write vault-internal readme file:
|
||||
String vaultReadmeFileName = resourceBundle.getString("addvault.new.readme.accessLocation.fileName");
|
||||
try (FileSystem fs = CryptoFileSystemProvider.newFileSystem(path, fsProps); //
|
||||
WritableByteChannel ch = Files.newByteChannel(fs.getPath("/", vaultReadmeFileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
|
||||
ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf()));
|
||||
// 3. write vault-internal readme file:
|
||||
String vaultReadmeFileName = resourceBundle.getString("addvault.new.readme.accessLocation.fileName");
|
||||
try (FileSystem fs = CryptoFileSystemProvider.newFileSystem(path, fsProps); //
|
||||
WritableByteChannel ch = Files.newByteChannel(fs.getPath("/", vaultReadmeFileName), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
|
||||
ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf()));
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
throw new IOException("Failed initialize vault.", e);
|
||||
}
|
||||
} catch (CryptoException e) {
|
||||
throw new IOException("Failed initialize vault.", e);
|
||||
}
|
||||
|
||||
// 4. write vault-external readme file:
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileLoaderContext;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
class StaticMasterkeyFileLoaderContext implements MasterkeyFileLoaderContext {
|
||||
|
||||
private final Path masterkeyFilePath;
|
||||
private final CharSequence passphrase;
|
||||
|
||||
StaticMasterkeyFileLoaderContext(Path masterkeyFilePath, CharSequence passphrase) {
|
||||
this.masterkeyFilePath = masterkeyFilePath;
|
||||
this.passphrase = passphrase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getCorrectMasterkeyFilePath(String s) {
|
||||
return masterkeyFilePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPassphrase(Path path) {
|
||||
Preconditions.checkArgument(masterkeyFilePath.equals(path));
|
||||
return passphrase;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,11 @@ package org.cryptomator.ui.keyloading;
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Subcomponent;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@KeyLoadingScoped
|
||||
@Subcomponent(modules = {KeyLoadingModule.class})
|
||||
|
||||
@@ -35,13 +35,13 @@ abstract class KeyLoadingModule {
|
||||
@Provides
|
||||
@KeyLoading
|
||||
@KeyLoadingScoped
|
||||
static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Optional<URI> keyId, Map<String, KeyLoadingStrategy> strategies) {
|
||||
static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Optional<URI> keyId, Map<String, Provider<KeyLoadingStrategy>> strategies) {
|
||||
if (keyId.isEmpty()) {
|
||||
return KeyLoadingStrategy.failed(new IllegalArgumentException("No key id provided"));
|
||||
} else {
|
||||
String scheme = keyId.get().getScheme();
|
||||
var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme));
|
||||
return strategies.getOrDefault(scheme, fallback);
|
||||
return strategies.getOrDefault(scheme, () -> fallback).get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
package org.cryptomator.ui.keyloading;
|
||||
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
|
||||
public interface KeyLoadingStrategy {
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* A reusable, stateful {@link MasterkeyLoader}, that can deal with certain exceptions.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface KeyLoadingStrategy extends MasterkeyLoader {
|
||||
|
||||
/**
|
||||
* @return A reusable masterkey loader, preconfigured with the vault of the current unlock process
|
||||
* @throws MasterkeyLoadingFailedException If unable to provide the masterkey loader
|
||||
*/
|
||||
MasterkeyLoader masterkeyLoader() throws MasterkeyLoadingFailedException;
|
||||
|
||||
/**
|
||||
* Allows the component to try and recover from an exception thrown while loading a masterkey.
|
||||
* Loads a master key. This might be a long-running operation, as it may require user input or expensive computations.
|
||||
* <p>
|
||||
* If loading fails exceptionally, this strategy might be able to {@link #recoverFromException(MasterkeyLoadingFailedException) recover from this exception}, so it can be used in a further attempt.
|
||||
*
|
||||
* @param exception An exception thrown either by {@link #masterkeyLoader()} or by the returned {@link MasterkeyLoader}.
|
||||
* @return <code>true</code> if this component was able to handle the exception and another attempt should be made to load a masterkey
|
||||
* @param keyId An URI uniquely identifying the source and identity of the key
|
||||
* @return The raw key bytes. Must not be null
|
||||
* @throws MasterkeyLoadingFailedException Thrown when it is impossible to fulfill the request
|
||||
*/
|
||||
@Override
|
||||
Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException;
|
||||
|
||||
/**
|
||||
* Allows the loader to try and recover from an exception thrown during the last attempt.
|
||||
*
|
||||
* @param exception An exception thrown by {@link #loadKey(URI)}.
|
||||
* @return <code>true</code> if this component was able to handle the exception and another attempt can be made to load a masterkey
|
||||
*/
|
||||
default boolean recoverFromException(MasterkeyLoadingFailedException exception) {
|
||||
return false;
|
||||
@@ -38,7 +51,7 @@ public interface KeyLoadingStrategy {
|
||||
* @return A new KeyLoadingStrategy that will always fail with an {@link MasterkeyLoadingFailedException}.
|
||||
*/
|
||||
static KeyLoadingStrategy failed(Exception exception) {
|
||||
return () -> {
|
||||
return keyid -> {
|
||||
if (exception instanceof MasterkeyLoadingFailedException e) {
|
||||
throw e;
|
||||
} else {
|
||||
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
package org.cryptomator.ui.keyloading.masterkeyfile;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileLoaderContext;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.UserInteractionLock;
|
||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingScoped;
|
||||
import org.cryptomator.ui.unlock.UnlockCancelledException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@KeyLoadingScoped
|
||||
class MasterkeyFileLoadingContext implements MasterkeyFileLoaderContext {
|
||||
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> passphraseEntryScene;
|
||||
private final Lazy<Scene> selectMasterkeyFileScene;
|
||||
private final UserInteractionLock<MasterkeyFileLoadingModule.PasswordEntry> passwordEntryLock;
|
||||
private final UserInteractionLock<MasterkeyFileLoadingModule.MasterkeyFileProvision> masterkeyFileProvisionLock;
|
||||
private final AtomicReference<char[]> password;
|
||||
private final AtomicReference<Path> filePath;
|
||||
|
||||
private boolean wrongPassword;
|
||||
|
||||
@Inject
|
||||
public MasterkeyFileLoadingContext(@KeyLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD) Lazy<Scene> passphraseEntryScene, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy<Scene> selectMasterkeyFileScene, UserInteractionLock<MasterkeyFileLoadingModule.PasswordEntry> passwordEntryLock, UserInteractionLock<MasterkeyFileLoadingModule.MasterkeyFileProvision> masterkeyFileProvisionLock, AtomicReference<char[]> password, AtomicReference<Path> filePath) {
|
||||
this.window = window;
|
||||
this.passphraseEntryScene = passphraseEntryScene;
|
||||
this.selectMasterkeyFileScene = selectMasterkeyFileScene;
|
||||
this.passwordEntryLock = passwordEntryLock;
|
||||
this.masterkeyFileProvisionLock = masterkeyFileProvisionLock;
|
||||
this.password = password;
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getCorrectMasterkeyFilePath(String masterkeyFilePath) {
|
||||
if (filePath.get() != null) { // e.g. already chosen on previous attempt with wrong password
|
||||
return filePath.get();
|
||||
}
|
||||
|
||||
assert filePath.get() == null;
|
||||
try {
|
||||
if (askForCorrectMasterkeyFile() == MasterkeyFileLoadingModule.MasterkeyFileProvision.MASTERKEYFILE_PROVIDED) {
|
||||
return filePath.get();
|
||||
} else {
|
||||
throw new UnlockCancelledException("Choosing masterkey file cancelled.");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new UnlockCancelledException("Choosing masterkey file interrupted", e);
|
||||
}
|
||||
}
|
||||
|
||||
private MasterkeyFileLoadingModule.MasterkeyFileProvision askForCorrectMasterkeyFile() throws InterruptedException {
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(selectMasterkeyFileScene.get());
|
||||
window.show();
|
||||
Window owner = window.getOwner();
|
||||
if (owner != null) {
|
||||
window.setX(owner.getX() + (owner.getWidth() - window.getWidth()) / 2);
|
||||
window.setY(owner.getY() + (owner.getHeight() - window.getHeight()) / 2);
|
||||
} else {
|
||||
window.centerOnScreen();
|
||||
}
|
||||
});
|
||||
return masterkeyFileProvisionLock.awaitInteraction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPassphrase(Path path) throws UnlockCancelledException {
|
||||
if (password.get() != null) { // e.g. pre-filled from keychain
|
||||
return CharBuffer.wrap(password.get());
|
||||
}
|
||||
|
||||
assert password.get() == null;
|
||||
try {
|
||||
if (askForPassphrase() == MasterkeyFileLoadingModule.PasswordEntry.PASSWORD_ENTERED) {
|
||||
assert password.get() != null;
|
||||
return CharBuffer.wrap(password.get());
|
||||
} else {
|
||||
throw new UnlockCancelledException("Password entry cancelled.");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new UnlockCancelledException("Password entry interrupted", e);
|
||||
}
|
||||
}
|
||||
|
||||
private MasterkeyFileLoadingModule.PasswordEntry askForPassphrase() throws InterruptedException {
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(passphraseEntryScene.get());
|
||||
window.show();
|
||||
Window owner = window.getOwner();
|
||||
if (owner != null) {
|
||||
window.setX(owner.getX() + (owner.getWidth() - window.getWidth()) / 2);
|
||||
window.setY(owner.getY() + (owner.getHeight() - window.getHeight()) / 2);
|
||||
} else {
|
||||
window.centerOnScreen();
|
||||
}
|
||||
if (wrongPassword) {
|
||||
Animations.createShakeWindowAnimation(window).play();
|
||||
}
|
||||
});
|
||||
return passwordEntryLock.awaitInteraction();
|
||||
}
|
||||
|
||||
public boolean recoverFromException(MasterkeyLoadingFailedException exception) {
|
||||
if (exception instanceof InvalidPassphraseException) {
|
||||
this.wrongPassword = true;
|
||||
password.set(null);
|
||||
return true; // reattempting key load
|
||||
} else {
|
||||
return false; // nothing we can do
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-9
@@ -7,8 +7,6 @@ import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.StringKey;
|
||||
import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileLoader;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
@@ -45,12 +43,6 @@ public abstract class MasterkeyFileLoadingModule {
|
||||
CANCELED
|
||||
}
|
||||
|
||||
@Provides
|
||||
@KeyLoadingScoped
|
||||
static MasterkeyFileLoader provideMasterkeyFileLoader(MasterkeyFileAccess masterkeyFileAccess, @KeyLoading Vault vault, MasterkeyFileLoadingContext context) {
|
||||
return masterkeyFileAccess.keyLoader(vault.getPath(), context);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@KeyLoadingScoped
|
||||
static UserInteractionLock<PasswordEntry> providePasswordEntryLock() {
|
||||
@@ -125,7 +117,7 @@ public abstract class MasterkeyFileLoadingModule {
|
||||
@Binds
|
||||
@IntoMap
|
||||
@KeyLoadingScoped
|
||||
@StringKey("masterkeyfile")
|
||||
@StringKey(MasterkeyFileLoadingStrategy.SCHEME)
|
||||
abstract KeyLoadingStrategy bindMasterkeyFileLoadingStrategy(MasterkeyFileLoadingStrategy strategy);
|
||||
|
||||
}
|
||||
|
||||
+122
-10
@@ -1,38 +1,150 @@
|
||||
package org.cryptomator.ui.keyloading.masterkeyfile;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileLoader;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.UserInteractionLock;
|
||||
import org.cryptomator.ui.keyloading.KeyLoading;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
import org.cryptomator.ui.unlock.UnlockCancelledException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import java.net.URI;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@KeyLoading
|
||||
class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
||||
public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
||||
|
||||
private final MasterkeyFileLoader masterkeyFileLoader;
|
||||
private final MasterkeyFileLoadingContext context;
|
||||
public static final String SCHEME = "masterkeyfile";
|
||||
|
||||
private final Vault vault;
|
||||
private final MasterkeyFileAccess masterkeyFileAcccess;
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> passphraseEntryScene;
|
||||
private final Lazy<Scene> selectMasterkeyFileScene;
|
||||
private final UserInteractionLock<MasterkeyFileLoadingModule.PasswordEntry> passwordEntryLock;
|
||||
private final UserInteractionLock<MasterkeyFileLoadingModule.MasterkeyFileProvision> masterkeyFileProvisionLock;
|
||||
private final AtomicReference<char[]> password;
|
||||
private final AtomicReference<Path> filePath;
|
||||
private final MasterkeyFileLoadingFinisher finisher;
|
||||
|
||||
private boolean wrongPassword;
|
||||
|
||||
@Inject
|
||||
public MasterkeyFileLoadingStrategy(MasterkeyFileLoader masterkeyFileLoader, MasterkeyFileLoadingContext context, MasterkeyFileLoadingFinisher finisher) {
|
||||
this.masterkeyFileLoader = masterkeyFileLoader;
|
||||
this.context = context;
|
||||
public MasterkeyFileLoadingStrategy(@KeyLoading Vault vault, MasterkeyFileAccess masterkeyFileAcccess, @KeyLoading Stage window, @FxmlScene(FxmlFile.UNLOCK_ENTER_PASSWORD) Lazy<Scene> passphraseEntryScene, @FxmlScene(FxmlFile.UNLOCK_SELECT_MASTERKEYFILE) Lazy<Scene> selectMasterkeyFileScene, UserInteractionLock<MasterkeyFileLoadingModule.PasswordEntry> passwordEntryLock, UserInteractionLock<MasterkeyFileLoadingModule.MasterkeyFileProvision> masterkeyFileProvisionLock, AtomicReference<char[]> password, AtomicReference<Path> filePath, MasterkeyFileLoadingFinisher finisher) {
|
||||
this.vault = vault;
|
||||
this.masterkeyFileAcccess = masterkeyFileAcccess;
|
||||
this.window = window;
|
||||
this.passphraseEntryScene = passphraseEntryScene;
|
||||
this.selectMasterkeyFileScene = selectMasterkeyFileScene;
|
||||
this.passwordEntryLock = passwordEntryLock;
|
||||
this.masterkeyFileProvisionLock = masterkeyFileProvisionLock;
|
||||
this.password = password;
|
||||
this.filePath = filePath;
|
||||
this.finisher = finisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MasterkeyFileLoader masterkeyLoader() {
|
||||
return masterkeyFileLoader;
|
||||
public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException {
|
||||
Preconditions.checkArgument(SCHEME.equalsIgnoreCase(keyId.getScheme()), "Only supports keys with scheme " + SCHEME);
|
||||
|
||||
try {
|
||||
Path filePath = vault.getPath().resolve(keyId.getSchemeSpecificPart());
|
||||
if (!Files.exists(filePath)) {
|
||||
filePath = getAlternateMasterkeyFilePath();
|
||||
}
|
||||
CharSequence passphrase = getPassphrase();
|
||||
return masterkeyFileAcccess.load(filePath, passphrase);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new UnlockCancelledException("Unlock interrupted", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean recoverFromException(MasterkeyLoadingFailedException exception) {
|
||||
return context.recoverFromException(exception);
|
||||
if (exception instanceof InvalidPassphraseException) {
|
||||
this.wrongPassword = true;
|
||||
password.set(null);
|
||||
return true; // reattempting key load
|
||||
} else {
|
||||
return false; // nothing we can do
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup(boolean unlockedSuccessfully) {
|
||||
finisher.cleanup(unlockedSuccessfully);
|
||||
}
|
||||
|
||||
private Path getAlternateMasterkeyFilePath() throws UnlockCancelledException, InterruptedException {
|
||||
if (filePath == null) {
|
||||
return switch (askUserForMasterkeyFilePath()) {
|
||||
case MASTERKEYFILE_PROVIDED -> filePath.get();
|
||||
case CANCELED -> throw new UnlockCancelledException("Choosing masterkey file cancelled.");
|
||||
};
|
||||
} else {
|
||||
return filePath.get();
|
||||
}
|
||||
}
|
||||
|
||||
private MasterkeyFileLoadingModule.MasterkeyFileProvision askUserForMasterkeyFilePath() throws InterruptedException {
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(selectMasterkeyFileScene.get());
|
||||
window.show();
|
||||
Window owner = window.getOwner();
|
||||
if (owner != null) {
|
||||
window.setX(owner.getX() + (owner.getWidth() - window.getWidth()) / 2);
|
||||
window.setY(owner.getY() + (owner.getHeight() - window.getHeight()) / 2);
|
||||
} else {
|
||||
window.centerOnScreen();
|
||||
}
|
||||
});
|
||||
return masterkeyFileProvisionLock.awaitInteraction();
|
||||
}
|
||||
|
||||
private CharSequence getPassphrase() throws UnlockCancelledException, InterruptedException {
|
||||
if (password.get() == null) {
|
||||
return switch (askForPassphrase()) {
|
||||
case PASSWORD_ENTERED -> CharBuffer.wrap(password.get());
|
||||
case CANCELED -> throw new UnlockCancelledException("Password entry cancelled.");
|
||||
};
|
||||
} else {
|
||||
// e.g. pre-filled from keychain or previous unlock attempt
|
||||
return CharBuffer.wrap(password.get());
|
||||
}
|
||||
}
|
||||
|
||||
private MasterkeyFileLoadingModule.PasswordEntry askForPassphrase() throws InterruptedException {
|
||||
Platform.runLater(() -> {
|
||||
window.setScene(passphraseEntryScene.get());
|
||||
window.show();
|
||||
Window owner = window.getOwner();
|
||||
if (owner != null) {
|
||||
window.setX(owner.getX() + (owner.getWidth() - window.getWidth()) / 2);
|
||||
window.setY(owner.getY() + (owner.getHeight() - window.getHeight()) / 2);
|
||||
} else {
|
||||
window.centerOnScreen();
|
||||
}
|
||||
if (wrongPassword) {
|
||||
Animations.createShakeWindowAnimation(window).play();
|
||||
}
|
||||
});
|
||||
return passwordEntryLock.awaitInteraction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
private void attemptUnlock() throws IOException, VolumeException, InvalidMountPointException, CryptoException {
|
||||
boolean success = false;
|
||||
try {
|
||||
vault.unlock(keyLoadingStrategy.masterkeyLoader());
|
||||
vault.unlock(keyLoadingStrategy);
|
||||
success = true;
|
||||
} catch (MasterkeyLoadingFailedException e) {
|
||||
if (keyLoadingStrategy.recoverFromException(e)) {
|
||||
|
||||
Reference in New Issue
Block a user