adjusted to new cryptolib/cryptofs API

This commit is contained in:
Sebastian Stenzel
2021-01-25 21:31:16 +01:00
parent 3f928cf958
commit 4b670a59a3
10 changed files with 114 additions and 78 deletions

View File

@@ -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")

View File

@@ -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<FileSystemFlags> 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 {

View File

@@ -24,7 +24,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.0.0-beta1</cryptomator.cryptofs.version>
<cryptomator.cryptofs.version>2.0.0-beta2</cryptomator.cryptofs.version>
<cryptomator.integrations.version>0.1.6</cryptomator.integrations.version>
<cryptomator.integrations.win.version>0.2.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>0.1.0-beta3</cryptomator.integrations.mac.version>
@@ -38,8 +38,8 @@
<commons-lang3.version>3.11</commons-lang3.version>
<jwt.version>3.11.0</jwt.version>
<easybind.version>2.1.0</easybind.version>
<guava.version>30.0-jre</guava.version>
<dagger.version>2.29.1</dagger.version>
<guava.version>30.1-jre</guava.version>
<dagger.version>2.31</dagger.version>
<gson.version>2.8.6</gson.version>
<slf4j.version>1.7.30</slf4j.version>
<logback.version>1.2.3</logback.version>

View File

@@ -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<CharSequence> password;
private final ReadmeGenerator readmeGenerator;
private final SecureRandom csprng;
private final MasterkeyFileAccess masterkeyFileAccess;
private final BooleanProperty processing;
private final BooleanProperty readyToCreateVault;
private final ObjectBinding<ContentDisplay> createVaultButtonState;
@@ -85,7 +80,7 @@ public class CreateNewVaultPasswordController implements FxController {
public Toggle skipRecoveryKey;
@Inject
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, @Named("newPassword") ObjectProperty<CharSequence> password, ReadmeGenerator readmeGenerator, SecureRandom csprng) {
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, @Named("newPassword") ObjectProperty<CharSequence> 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");

View File

@@ -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;
}
}

View File

@@ -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<CharSequence> newPassword, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng) {
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty<CharSequence> 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);

View File

@@ -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<String> 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);
}

View File

@@ -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<Boolean> {
public class UnlockWorkflow extends Task<Boolean> implements MasterkeyFileLoaderContext {
private static final Logger LOG = LoggerFactory.getLogger(UnlockWorkflow.class);
@@ -59,9 +64,10 @@ public class UnlockWorkflow extends Task<Boolean> {
private final Lazy<Scene> successScene;
private final Lazy<Scene> invalidMountPointScene;
private final ErrorComponent.Builder errorComponent;
private final MasterkeyFileAccess masterkeyFileAccess;
@Inject
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference<char[]> password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional<char[]> savedPassword, UserInteractionLock<PasswordEntry> passwordEntryLock, KeychainManager keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy<Scene> unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, ErrorComponent.Builder errorComponent) {
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference<char[]> password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional<char[]> savedPassword, UserInteractionLock<PasswordEntry> passwordEntryLock, KeychainManager keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy<Scene> unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, ErrorComponent.Builder errorComponent, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
this.vault = vault;
this.vaultService = vaultService;
@@ -74,6 +80,7 @@ public class UnlockWorkflow extends Task<Boolean> {
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> {
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<Boolean> {
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<Boolean> {
protected void cancelled() {
vault.setState(VaultState.LOCKED);
}
}

View File

@@ -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/)

View File

@@ -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<MasterkeyFile> 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");