diff --git a/main/pom.xml b/main/pom.xml
index 0ba69d20f..edd0b1c91 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -24,7 +24,7 @@
UTF-8
- 2.0.0-beta2
+ 2.0.0-beta3
0.1.6
0.2.1
0.1.0-beta3
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/StaticMasterkeyFileLoaderContext.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/StaticMasterkeyFileLoaderContext.java
index 4ccc06e3e..13c3b05b1 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/StaticMasterkeyFileLoaderContext.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/StaticMasterkeyFileLoaderContext.java
@@ -16,7 +16,7 @@ class StaticMasterkeyFileLoaderContext implements MasterkeyFileLoaderContext {
}
@Override
- public Path getMasterkeyFilePath(String s) {
+ public Path getCorrectMasterkeyFilePath(String s) {
return masterkeyFilePath;
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
index 0e6baf032..8c250665a 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
@@ -10,8 +10,8 @@ import org.cryptomator.common.vaults.Volume.VolumeException;
import org.cryptomator.cryptolib.api.CryptoException;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
+import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
-import org.cryptomator.cryptolib.common.MasterkeyFileLoader;
import org.cryptomator.cryptolib.common.MasterkeyFileLoaderContext;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
@@ -66,6 +66,8 @@ public class UnlockWorkflow extends Task implements MasterkeyFileLoader
private final ErrorComponent.Builder errorComponent;
private final MasterkeyFileAccess masterkeyFileAccess;
+ private boolean didEnterWrongPassphrase = false;
+
@Inject
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, KeychainManager keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, ErrorComponent.Builder errorComponent, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
@@ -95,43 +97,57 @@ public class UnlockWorkflow extends Task implements MasterkeyFileLoader
@Override
protected Boolean call() throws InterruptedException, IOException, VolumeException, InvalidMountPointException, CryptoException {
try {
- if (attemptUnlock()) {
- handleSuccess();
- return true;
- } else {
- cancel(false); // set Tasks state to cancelled
- return false;
- }
+ MasterkeyLoader keyLoader = masterkeyFileAccess.keyLoader(vault.getPath(), this);
+ attemptUnlock(keyLoader, 0);
+ handleSuccess();
+ return true;
+ } catch (PasswordEntryCancelledException e) {
+ cancel(false); // set Tasks state to cancelled
+ return false;
} finally {
wipePassword(password.get());
wipePassword(savedPassword.orElse(null));
}
}
- private boolean attemptUnlock() throws InterruptedException, IOException, VolumeException, InvalidMountPointException, CryptoException {
- boolean proceed = password.get() != null || askForPassword(false) == PasswordEntry.PASSWORD_ENTERED;
- while (proceed) {
- try {
- vault.unlock(masterkeyFileAccess.keyLoader(vault.getPath(), this));
- return true;
- } catch (InvalidPassphraseException e) {
- proceed = askForPassword(true) == PasswordEntry.PASSWORD_ENTERED;
- }
+ private void attemptUnlock(MasterkeyLoader keyLoader, int attempt) throws IOException, VolumeException, InvalidMountPointException, CryptoException {
+ try {
+ vault.unlock(keyLoader);
+ } catch (InvalidPassphraseException e) {
+ LOG.info("Unlock attempt #{} failed due to incorrect password", attempt);
+ wipePassword(password.getAndSet(null));
+ didEnterWrongPassphrase = true;
+ attemptUnlock(keyLoader, attempt + 1);
}
- return false;
}
@Override
- public Path getMasterkeyFilePath(String masterkeyFilePath) {
- return null; // TODO non-standard paths not yet supported (cancel unlock attempt)
+ public Path getCorrectMasterkeyFilePath(String masterkeyFilePath) {
+ LOG.warn("Did not find masterkey file at expected path: {}", masterkeyFilePath);
+ throw new MasterkeyLoadingFailedException(masterkeyFilePath + " not found.", new UnsupportedOperationException("getCorrectMasterkeyFilePath() not implemented"));
}
@Override
- public CharSequence getPassphrase(Path path) {
- return CharBuffer.wrap(password.get());
+ public CharSequence getPassphrase(Path path) throws PasswordEntryCancelledException {
+ if (password.get() != null) { // e.g. pre-filled from keychain
+ return CharBuffer.wrap(password.get());
+ }
+
+ assert password.get() == null;
+ try {
+ if (askForPassphrase() == PasswordEntry.PASSWORD_ENTERED) {
+ assert password.get() != null;
+ return CharBuffer.wrap(password.get());
+ } else {
+ throw new PasswordEntryCancelledException("Password entry cancelled.");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new PasswordEntryCancelledException("Password entry interrupted", e);
+ }
}
- private PasswordEntry askForPassword(boolean animateShake) throws InterruptedException {
+ private PasswordEntry askForPassphrase() throws InterruptedException {
Platform.runLater(() -> {
window.setScene(unlockScene.get());
window.show();
@@ -142,7 +158,7 @@ public class UnlockWorkflow extends Task implements MasterkeyFileLoader
} else {
window.centerOnScreen();
}
- if (animateShake) {
+ if (didEnterWrongPassphrase) {
Animations.createShakeWindowAnimation(window).play();
}
});
@@ -191,15 +207,12 @@ public class UnlockWorkflow extends Task implements MasterkeyFileLoader
LOG.error("Unlock failed. Mountpoint doesn't exist (needs to be a folder): {}", cause.getMessage());
}
showInvalidMountPointScene();
- return;
} else if (cause instanceof FileAlreadyExistsException) {
LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage());
showInvalidMountPointScene();
- return;
} else if (cause instanceof DirectoryNotEmptyException) {
LOG.error("Unlock failed. Mountpoint not an empty directory: {}", cause.getMessage());
showInvalidMountPointScene();
- return;
} else {
handleGenericError(impExc);
}
@@ -241,4 +254,14 @@ public class UnlockWorkflow extends Task implements MasterkeyFileLoader
protected void cancelled() {
vault.setState(VaultState.LOCKED);
}
+
+ private static class PasswordEntryCancelledException extends MasterkeyLoadingFailedException {
+ public PasswordEntryCancelledException(String message) {
+ super(message);
+ }
+
+ public PasswordEntryCancelledException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
}