Parcourir la source

adjusted to new cryptolib/cryptofs API

Sebastian Stenzel il y a 4 ans
Parent
commit
4b670a59a3

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

+ 26 - 31
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<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,19 +129,21 @@ public class Vault {
 		}
 	}
 
-	public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, VolumeException, InvalidMountPointException {
-		if (cryptoFileSystem.get() == null) {
-			CryptoFileSystem fs = createCryptoFileSystem(passphrase);
+	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);
-			try {
-				volume = volumeProvider.get();
-				volume.mount(fs, getEffectiveMountFlags());
-			} catch (IOException | InvalidMountPointException | VolumeException e) {
-				destroyCryptoFileSystem();
-				throw e;
+			volume = volumeProvider.get();
+			volume.mount(fs, getEffectiveMountFlags());
+			success = true;
+		} finally {
+			if (!success) {
+				destroyCryptoFileSystem(fs);
 			}
-		} else {
-			throw new IllegalStateException("Already unlocked.");
 		}
 	}
 
@@ -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 {

+ 3 - 3
main/pom.xml

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

Fichier diff supprimé car celui-ci est trop grand
+ 11 - 14
main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java


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

+ 5 - 6
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<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);

+ 6 - 5
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<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);
 		}

Fichier diff supprimé car celui-ci est trop grand
+ 20 - 4
main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java


+ 2 - 2
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/)

+ 5 - 12
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<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");