diff --git a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java index 0ea3428ed..9126b758b 100644 --- a/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/CommonsModule.java @@ -15,6 +15,7 @@ import org.cryptomator.common.settings.SettingsProvider; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultComponent; import org.cryptomator.common.vaults.VaultListManager; +import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.cryptomator.frontend.webdav.WebDavServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +66,12 @@ public abstract class CommonsModule { } } + @Provides + @Singleton + static MasterkeyFileAccess provideMasterkeyFileAccess(SecureRandom csprng) { + return new MasterkeyFileAccess(Constants.PEPPER, csprng); + } + @Provides @Singleton @Named("SemVer") diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java index 44c6897e4..0dff26096 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -20,8 +20,8 @@ import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.common.Constants; import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptolib.api.CryptoException; -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.cryptolib.common.MasterkeyFile; +import org.cryptomator.cryptolib.api.MasterkeyLoader; +import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +36,6 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import java.io.IOException; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumSet; @@ -45,9 +44,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; -import static org.cryptomator.common.Constants.PEPPER; - @PerVault public class Vault { @@ -101,7 +97,7 @@ public class Vault { // Commands // ********************************************************************************/ - private CryptoFileSystem createCryptoFileSystem(CharSequence passphrase) throws NoSuchFileException, IOException, InvalidPassphraseException, CryptoException { + private CryptoFileSystem createCryptoFileSystem(MasterkeyLoader keyLoader) throws IOException, MasterkeyLoadingFailedException { Set flags = EnumSet.noneOf(FileSystemFlags.class); if (vaultSettings.usesReadOnlyMode().get()) { flags.add(FileSystemFlags.READONLY); @@ -114,20 +110,16 @@ public class Vault { } assert vaultSettings.filenameLengthLimit().get() > 0; - Path masterkeyPath = getPath().resolve(MASTERKEY_FILENAME); - try (var keyLoader = MasterkeyFile.withContentFromFile(masterkeyPath).unlock(passphrase, PEPPER, Optional.empty())) { - CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() // - .withKeyLoader(keyLoader) // - .withFlags(flags) // - .withMaxPathLength(vaultSettings.filenameLengthLimit().get() + Constants.MAX_ADDITIONAL_PATH_LENGTH) // - .withMaxNameLength(vaultSettings.filenameLengthLimit().get()) // - .build(); - return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); - } + CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() // + .withKeyLoaders(keyLoader) // + .withFlags(flags) // + .withMaxPathLength(vaultSettings.filenameLengthLimit().get() + Constants.MAX_ADDITIONAL_PATH_LENGTH) // + .withMaxNameLength(vaultSettings.filenameLengthLimit().get()) // + .build(); + return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); } - private void destroyCryptoFileSystem() { - CryptoFileSystem fs = cryptoFileSystem.getAndSet(null); + private void destroyCryptoFileSystem(CryptoFileSystem fs) { if (fs != null) { try { fs.close(); @@ -137,20 +129,22 @@ public class Vault { } } - public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, VolumeException, InvalidMountPointException { - if (cryptoFileSystem.get() == null) { - CryptoFileSystem fs = createCryptoFileSystem(passphrase); - cryptoFileSystem.set(fs); - try { - volume = volumeProvider.get(); - volume.mount(fs, getEffectiveMountFlags()); - } catch (IOException | InvalidMountPointException | VolumeException e) { - destroyCryptoFileSystem(); - throw e; - } - } else { + public synchronized void unlock(MasterkeyLoader keyLoader) throws CryptoException, IOException, VolumeException, InvalidMountPointException { + if (cryptoFileSystem.get() != null) { throw new IllegalStateException("Already unlocked."); } + CryptoFileSystem fs = createCryptoFileSystem(keyLoader); + boolean success = false; + try { + cryptoFileSystem.set(fs); + volume = volumeProvider.get(); + volume.mount(fs, getEffectiveMountFlags()); + success = true; + } finally { + if (!success) { + destroyCryptoFileSystem(fs); + } + } } public synchronized void lock(boolean forced) throws VolumeException { @@ -159,7 +153,8 @@ public class Vault { } else { volume.unmount(); } - destroyCryptoFileSystem(); + CryptoFileSystem fs = cryptoFileSystem.getAndSet(null); + destroyCryptoFileSystem(fs); } public void reveal(Volume.Revealer vaultRevealer) throws VolumeException { diff --git a/main/pom.xml b/main/pom.xml index 26a093185..b23747cd8 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -24,7 +24,7 @@ UTF-8 - 2.0.0-beta1 + 2.0.0-beta2 0.1.6 0.2.1 0.1.0-beta3 @@ -38,8 +38,8 @@ 3.11 3.11.0 2.1.0 - 30.0-jre - 2.29.1 + 30.1-jre + 2.31 2.8.6 1.7.30 1.2.3 diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java index 84dfbd02e..e4f02ac8e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java @@ -7,10 +7,8 @@ import org.cryptomator.cryptofs.CryptoFileSystemProperties; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.VaultCipherCombo; import org.cryptomator.cryptolib.api.CryptoException; -import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.Masterkey; -import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException; -import org.cryptomator.cryptolib.common.MasterkeyFile; +import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.cryptomator.cryptolib.common.MasterkeyFileLoader; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; @@ -36,7 +34,6 @@ import javafx.scene.control.ContentDisplay; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.stage.Stage; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.channels.WritableByteChannel; @@ -46,14 +43,11 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.security.SecureRandom; -import java.util.Collections; -import java.util.Optional; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; import static java.nio.charset.StandardCharsets.US_ASCII; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; -import static org.cryptomator.common.Constants.PEPPER; @AddVaultWizardScoped public class CreateNewVaultPasswordController implements FxController { @@ -76,6 +70,7 @@ public class CreateNewVaultPasswordController implements FxController { private final ObjectProperty password; private final ReadmeGenerator readmeGenerator; private final SecureRandom csprng; + private final MasterkeyFileAccess masterkeyFileAccess; private final BooleanProperty processing; private final BooleanProperty readyToCreateVault; private final ObjectBinding createVaultButtonState; @@ -85,7 +80,7 @@ public class CreateNewVaultPasswordController implements FxController { public Toggle skipRecoveryKey; @Inject - CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, @Named("newPassword") ObjectProperty password, ReadmeGenerator readmeGenerator, SecureRandom csprng) { + CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty vaultPath, @AddVaultWizardWindow ObjectProperty vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, @Named("newPassword") ObjectProperty password, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) { this.window = window; this.chooseLocationScene = chooseLocationScene; this.recoveryKeyScene = recoveryKeyScene; @@ -102,6 +97,7 @@ public class CreateNewVaultPasswordController implements FxController { this.password = password; this.readmeGenerator = readmeGenerator; this.csprng = csprng; + this.masterkeyFileAccess = masterkeyFileAccess; this.processing = new SimpleBooleanProperty(); this.readyToCreateVault = new SimpleBooleanProperty(); this.createVaultButtonState = Bindings.createObjectBinding(this::getCreateVaultButtonState, processing); @@ -177,14 +173,15 @@ public class CreateNewVaultPasswordController implements FxController { // 1. write masterkey: Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME); try (Masterkey masterkey = Masterkey.createNew(csprng)) { - byte[] serialized = MasterkeyFile.lock(masterkey, passphrase, PEPPER, 999, csprng); - Files.write(masterkeyFilePath, serialized, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); + masterkeyFileAccess.persist(masterkey, masterkeyFilePath, passphrase); } - // 2. verify masterkey and initialize vault: - try (var loader = MasterkeyFile.withContentFromFile(masterkeyFilePath).unlock(passphrase, PEPPER, Optional.of(999))) { - CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(VaultCipherCombo.SIV_CTRMAC).withKeyLoader(loader).build(); - CryptoFileSystemProvider.initialize(path, fsProps, MasterkeyFileLoader.KEY_ID); + // 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)); // 3. write vault-internal readme file: String vaultReadmeFileName = resourceBundle.getString("addvault.new.readme.accessLocation.fileName"); 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 new file mode 100644 index 000000000..4ccc06e3e --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/StaticMasterkeyFileLoaderContext.java @@ -0,0 +1,28 @@ +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 getMasterkeyFilePath(String s) { + return masterkeyFilePath; + } + + @Override + public CharSequence getPassphrase(Path path) { + Preconditions.checkArgument(masterkeyFilePath.equals(path)); + return passphrase; + } +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java index 436103436..93c7e0576 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java @@ -2,11 +2,10 @@ package org.cryptomator.ui.changepassword; import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptofs.common.MasterkeyBackupHelper; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.cryptolib.common.MasterkeyFile; +import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.ErrorComponent; @@ -31,8 +30,6 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.security.SecureRandom; -import java.text.Normalizer; -import java.util.Optional; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; @@ -48,19 +45,21 @@ public class ChangePasswordController implements FxController { private final ErrorComponent.Builder errorComponent; private final KeychainManager keychain; private final SecureRandom csprng; + private final MasterkeyFileAccess masterkeyFileAccess; public NiceSecurePasswordField oldPasswordField; public CheckBox finalConfirmationCheckbox; public Button finishButton; @Inject - public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty newPassword, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng) { + public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty newPassword, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) { this.window = window; this.vault = vault; this.newPassword = newPassword; this.errorComponent = errorComponent; this.keychain = keychain; this.csprng = csprng; + this.masterkeyFileAccess = masterkeyFileAccess; } @FXML @@ -85,7 +84,7 @@ public class ChangePasswordController implements FxController { CharSequence newPassphrase = newPassword.get(); // TODO verify: is this already NFC-normalized? Path masterkeyPath = vault.getPath().resolve(MASTERKEY_FILENAME); byte[] oldMasterkeyBytes = Files.readAllBytes(masterkeyPath); - byte[] newMasterkeyBytes = MasterkeyFile.changePassphrase(oldMasterkeyBytes, oldPassphrase, newPassphrase, new byte[0], csprng); + byte[] newMasterkeyBytes = masterkeyFileAccess.changePassphrase(oldMasterkeyBytes, oldPassphrase, newPassphrase); Path backupKeyPath = vault.getPath().resolve(MASTERKEY_FILENAME + MasterkeyBackupHelper.generateFileIdSuffix(oldMasterkeyBytes) + MASTERKEY_BACKUP_SUFFIX); Files.move(masterkeyPath, backupKeyPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); Files.write(masterkeyPath, newMasterkeyBytes, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); diff --git a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java index a53113020..28aaa1d71 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java +++ b/main/ui/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java @@ -6,7 +6,7 @@ import org.cryptomator.cryptofs.common.MasterkeyBackupHelper; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.Masterkey; -import org.cryptomator.cryptolib.common.MasterkeyFile; +import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import javax.inject.Inject; import javax.inject.Singleton; @@ -30,11 +30,13 @@ public class RecoveryKeyFactory { private final WordEncoder wordEncoder; private final SecureRandom csprng; + private final MasterkeyFileAccess masterkeyFileAccess; @Inject - public RecoveryKeyFactory(WordEncoder wordEncoder, SecureRandom csprng) { + public RecoveryKeyFactory(WordEncoder wordEncoder, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) { this.wordEncoder = wordEncoder; this.csprng = csprng; + this.masterkeyFileAccess = masterkeyFileAccess; } public Collection getDictionary() { @@ -53,7 +55,7 @@ public class RecoveryKeyFactory { public String createRecoveryKey(Path vaultPath, CharSequence password) throws IOException, InvalidPassphraseException, CryptoException { Path masterkeyPath = vaultPath.resolve(MASTERKEY_FILENAME); byte[] rawKey = new byte[0]; - try (var masterkey = MasterkeyFile.withContentFromFile(masterkeyPath).unlock(password, PEPPER, Optional.empty()).loadKeyAndClose()) { + try (var masterkey = masterkeyFileAccess.load(masterkeyPath, password)) { rawKey = masterkey.getEncoded(); return createRecoveryKey(rawKey); } finally { @@ -87,14 +89,13 @@ public class RecoveryKeyFactory { public void resetPasswordWithRecoveryKey(Path vaultPath, String recoveryKey, CharSequence newPassword) throws IOException, IllegalArgumentException { final byte[] rawKey = decodeRecoveryKey(recoveryKey); try (var masterkey = Masterkey.createFromRaw(rawKey)) { - byte[] restoredKey = MasterkeyFile.lock(masterkey, newPassword, PEPPER, 999, csprng); Path masterkeyPath = vaultPath.resolve(MASTERKEY_FILENAME); if (Files.exists(masterkeyPath)) { byte[] oldMasterkeyBytes = Files.readAllBytes(masterkeyPath); Path backupKeyPath = vaultPath.resolve(MASTERKEY_FILENAME + MasterkeyBackupHelper.generateFileIdSuffix(oldMasterkeyBytes) + MASTERKEY_BACKUP_SUFFIX); Files.move(masterkeyPath, backupKeyPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); } - Files.write(masterkeyPath, restoredKey, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE); + masterkeyFileAccess.persist(masterkey, masterkeyPath, newPassword); } finally { Arrays.fill(rawKey, (byte) 0x00); } 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 475ba92a5..1624ba45f 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 @@ -9,6 +9,10 @@ import org.cryptomator.common.vaults.VaultState; 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.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; import org.cryptomator.ui.common.ErrorComponent; @@ -32,6 +36,7 @@ import java.nio.CharBuffer; import java.nio.file.DirectoryNotEmptyException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.NotDirectoryException; +import java.nio.file.Path; import java.util.Arrays; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; @@ -43,7 +48,7 @@ import java.util.concurrent.atomic.AtomicReference; * This class runs the unlock process and controls when to display which UI. */ @UnlockScoped -public class UnlockWorkflow extends Task { +public class UnlockWorkflow extends Task implements MasterkeyFileLoaderContext { private static final Logger LOG = LoggerFactory.getLogger(UnlockWorkflow.class); @@ -59,9 +64,10 @@ public class UnlockWorkflow extends Task { private final Lazy successScene; private final Lazy invalidMountPointScene; private final ErrorComponent.Builder errorComponent; + private final MasterkeyFileAccess masterkeyFileAccess; @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) { + 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; this.vault = vault; this.vaultService = vaultService; @@ -74,6 +80,7 @@ public class UnlockWorkflow extends Task { this.successScene = successScene; this.invalidMountPointScene = invalidMountPointScene; this.errorComponent = errorComponent; + this.masterkeyFileAccess = masterkeyFileAccess; setOnFailed(event -> { Throwable throwable = event.getSource().getException(); @@ -105,7 +112,7 @@ public class UnlockWorkflow extends Task { boolean proceed = password.get() != null || askForPassword(false) == PasswordEntry.PASSWORD_ENTERED; while (proceed) { try { - vault.unlock(CharBuffer.wrap(password.get())); + vault.unlock(masterkeyFileAccess.keyLoader(vault.getPath(), this)); return true; } catch (InvalidPassphraseException e) { proceed = askForPassword(true) == PasswordEntry.PASSWORD_ENTERED; @@ -114,6 +121,16 @@ public class UnlockWorkflow extends Task { return false; } + @Override + public Path getMasterkeyFilePath(String masterkeyFilePath) { + return null; // TODO non-standard paths not yet supported (cancel unlock attempt) + } + + @Override + public CharSequence getPassphrase(Path path) { + return CharBuffer.wrap(password.get()); + } + private PasswordEntry askForPassword(boolean animateShake) throws InterruptedException { Platform.runLater(() -> { window.setScene(unlockScene.get()); @@ -238,5 +255,4 @@ public class UnlockWorkflow extends Task { protected void cancelled() { vault.setState(VaultState.LOCKED); } - } diff --git a/main/ui/src/main/resources/license/THIRD-PARTY.txt b/main/ui/src/main/resources/license/THIRD-PARTY.txt index 30a364776..6d60273ac 100644 --- a/main/ui/src/main/resources/license/THIRD-PARTY.txt +++ b/main/ui/src/main/resources/license/THIRD-PARTY.txt @@ -19,10 +19,10 @@ Cryptomator uses 46 third-party dependencies under the following licenses: - jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - http://github.com/jnr/jnr-ffi) - FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/) - Gson (com.google.code.gson:gson:2.8.6 - https://github.com/google/gson/gson) - - Dagger (com.google.dagger:dagger:2.29.1 - https://github.com/google/dagger) + - Dagger (com.google.dagger:dagger:2.31 - https://github.com/google/dagger) - error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations) - Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess) - - Guava: Google Core Libraries for Java (com.google.guava:guava:30.0-jre - https://github.com/google/guava/guava) + - Guava: Google Core Libraries for Java (com.google.guava:guava:30.1-jre - https://github.com/google/guava/guava) - Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - https://github.com/google/guava/listenablefuture) - J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - https://github.com/google/j2objc/) - Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/) diff --git a/main/ui/src/test/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactoryTest.java b/main/ui/src/test/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactoryTest.java index a8c7d4844..88cd41b0b 100644 --- a/main/ui/src/test/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactoryTest.java +++ b/main/ui/src/test/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactoryTest.java @@ -1,16 +1,12 @@ package org.cryptomator.ui.recoverykey; import com.google.common.base.Splitter; -import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.Masterkey; -import org.cryptomator.cryptolib.common.MasterkeyFile; -import org.cryptomator.cryptolib.common.MasterkeyFileLoader; +import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.mockito.MockedStatic; import org.mockito.Mockito; import java.io.IOException; @@ -21,19 +17,16 @@ class RecoveryKeyFactoryTest { private WordEncoder wordEncoder = new WordEncoder(); private SecureRandom csprng = Mockito.mock(SecureRandom.class); - private RecoveryKeyFactory inTest = new RecoveryKeyFactory(wordEncoder, csprng); + private MasterkeyFileAccess masterkeyFileAccess = Mockito.mock(MasterkeyFileAccess.class); + private RecoveryKeyFactory inTest = new RecoveryKeyFactory(wordEncoder, csprng, masterkeyFileAccess); @Test @DisplayName("createRecoveryKey() creates 44 words") public void testCreateRecoveryKey() throws IOException, CryptoException { Path pathToVault = Path.of("path/to/vault"); - MockedStatic masterkeyFileClass = Mockito.mockStatic(MasterkeyFile.class); - MasterkeyFile masterkeyFile = Mockito.mock(MasterkeyFile.class); - MasterkeyFileLoader keyLoader = Mockito.mock(MasterkeyFileLoader.class); Masterkey masterkey = Mockito.mock(Masterkey.class); - masterkeyFileClass.when(() -> MasterkeyFile.withContentFromFile(Path.of("path/to/vault/masterkey.cryptomator"))).thenReturn(masterkeyFile); - Mockito.when(masterkeyFile.unlock(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(keyLoader); - Mockito.when(keyLoader.loadKeyAndClose()).thenReturn(masterkey); + Mockito.when(masterkeyFileAccess.load(pathToVault.resolve("masterkey.cryptomator"), "asd")).thenReturn(masterkey); + Mockito.when(masterkey.getEncoded()).thenReturn(new byte[64]); String recoveryKey = inTest.createRecoveryKey(pathToVault, "asd");