Browse Source

New vault format: Passwords are now strictly used in unicode NFC when passed to key derivation. References #521

Sebastian Stenzel 7 years ago
parent
commit
3d030cb6b0

+ 1 - 1
main/pom.xml

@@ -28,7 +28,7 @@
 
 		<!-- dependency versions -->
 		<cryptomator.cryptolib.version>1.1.2</cryptomator.cryptolib.version>
-		<cryptomator.cryptofs.version>1.3.0</cryptomator.cryptofs.version>
+		<cryptomator.cryptofs.version>1.4.0</cryptomator.cryptofs.version>
 		<cryptomator.webdav.version>0.6.1</cryptomator.webdav.version>
 		<cryptomator.jni.version>1.0.2</cryptomator.jni.version>
 		<slf4j.version>1.7.25</slf4j.version>

+ 2 - 2
main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java

@@ -19,8 +19,8 @@ public class UpgradeStrategies {
 	private final Collection<UpgradeStrategy> strategies;
 
 	@Inject
-	public UpgradeStrategies(UpgradeVersion3DropBundleExtension upgrader1, UpgradeVersion3to4 upgrader2, UpgradeVersion4to5 upgrader3) {
-		strategies = Collections.unmodifiableList(Arrays.asList(upgrader1, upgrader2, upgrader3));
+	public UpgradeStrategies(UpgradeVersion3DropBundleExtension upgrader1, UpgradeVersion3to4 upgrader2, UpgradeVersion4to5 upgrader3, UpgradeVersion5toX upgrader4) {
+		strategies = Collections.unmodifiableList(Arrays.asList(upgrader1, upgrader2, upgrader3, upgrader4));
 	}
 
 	public UpgradeStrategy getUpgradeStrategy(Vault vault) {

+ 17 - 7
main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java

@@ -63,18 +63,17 @@ public abstract class UpgradeStrategy {
 	 */
 	public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException {
 		LOG.info("Upgrading {} from {} to {}.", vault.getPath(), vaultVersionBeforeUpgrade, vaultVersionAfterUpgrade);
-		final Path masterkeyFile = vault.getPath().resolve(MASTERKEY_FILENAME);
-		try (Cryptor cryptor = cryptorProvider.createFromKeyFile(KeyFile.parse(Files.readAllBytes(masterkeyFile)), passphrase, vaultVersionBeforeUpgrade)) {
+		final Path masterkeyFileBeforeUpgrade = vault.getPath().resolve(MASTERKEY_FILENAME);
+		try (Cryptor cryptor = readMasterkeyFile(masterkeyFileBeforeUpgrade, passphrase)) {
 			// create backup, as soon as we know the password was correct:
-			final Path masterkeyBackupFile = vault.getPath().resolve(MASTERKEY_BACKUP_FILENAME);
-			Files.copy(masterkeyFile, masterkeyBackupFile, StandardCopyOption.REPLACE_EXISTING);
+			Path masterkeyBackupFile = vault.getPath().resolve(MASTERKEY_BACKUP_FILENAME);
+			Files.copy(masterkeyFileBeforeUpgrade, masterkeyBackupFile, StandardCopyOption.REPLACE_EXISTING);
 			LOG.info("Backuped masterkey.");
 			// do stuff:
 			upgrade(vault, cryptor);
 			// write updated masterkey file:
-			final byte[] upgradedMasterkeyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase, vaultVersionAfterUpgrade).serialize();
-			final Path masterkeyFileAfterUpgrade = vault.getPath().resolve(MASTERKEY_FILENAME); // path may have changed
-			Files.write(masterkeyFileAfterUpgrade, upgradedMasterkeyFileContents, StandardOpenOption.TRUNCATE_EXISTING);
+			Path masterkeyFileAfterUpgrade = vault.getPath().resolve(MASTERKEY_FILENAME); // path may have changed
+			writeMasterkeyFile(masterkeyFileAfterUpgrade, cryptor, passphrase);
 			LOG.info("Updated masterkey.");
 		} catch (InvalidPassphraseException e) {
 			throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword"));
@@ -92,6 +91,17 @@ public abstract class UpgradeStrategy {
 		}
 	}
 
+	protected Cryptor readMasterkeyFile(Path masterkeyFile, CharSequence passphrase) throws UnsupportedVaultFormatException, InvalidPassphraseException, IOException {
+		byte[] fileContents = Files.readAllBytes(masterkeyFile);
+		KeyFile keyFile = KeyFile.parse(fileContents);
+		return cryptorProvider.createFromKeyFile(keyFile, passphrase, vaultVersionBeforeUpgrade);
+	}
+
+	protected void writeMasterkeyFile(Path masterkeyFile, Cryptor cryptor, CharSequence passphrase) throws IOException {
+		byte[] fileContents = cryptor.writeKeysToMasterkeyFile(passphrase, vaultVersionAfterUpgrade).serialize();
+		Files.write(masterkeyFile, fileContents, StandardOpenOption.TRUNCATE_EXISTING);
+	}
+
 	protected abstract void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException;
 
 	/**

+ 70 - 0
main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion5toX.java

@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the accompanying LICENSE file.
+ *******************************************************************************/
+package org.cryptomator.ui.model;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.cryptomator.cryptofs.migration.Migrators;
+import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException;
+import org.cryptomator.cryptolib.Cryptors;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.ui.l10n.Localization;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+class UpgradeVersion5toX extends UpgradeStrategy {
+
+	private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion5toX.class);
+
+	@Inject
+	public UpgradeVersion5toX(Localization localization) {
+		super(Cryptors.version1(UpgradeStrategy.strongSecureRandom()), localization, 5, Integer.MAX_VALUE);
+	}
+
+	@Override
+	public String getTitle(Vault vault) {
+		return localization.getString("upgrade.version5toX.title");
+	}
+
+	@Override
+	public String getMessage(Vault vault) {
+		return localization.getString("upgrade.version5toX.msg");
+	}
+
+	@Override
+	public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException {
+		try {
+			Migrators.get().migrate(vault.getPath(), "masterkey.cryptomator", passphrase);
+		} catch (InvalidPassphraseException e) {
+			throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword"));
+		} catch (NoApplicableMigratorException | IOException e) {
+			LOG.warn("Upgrade failed.", e);
+			throw new UpgradeFailedException("Upgrade failed. Details in log message.");
+		}
+	}
+
+	@Override
+	protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
+		// called by #upgrade(Vault, CharSequence), which got overwritten.
+		throw new AssertionError("Method can not be called.");
+	}
+
+	@Override
+	public boolean isApplicable(Vault vault) {
+		try {
+			return Migrators.get().needsMigration(vault.getPath(), "masterkey.cryptomator");
+		} catch (IOException e) {
+			LOG.warn("Could not determine, whether upgrade is applicable.", e);
+			return false;
+		}
+	}
+
+}

+ 4 - 6
main/ui/src/main/java/org/cryptomator/ui/model/Vault.java

@@ -16,7 +16,6 @@ import java.nio.file.FileSystem;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.EnumSet;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
@@ -32,7 +31,6 @@ import org.cryptomator.common.settings.Settings;
 import org.cryptomator.common.settings.VaultSettings;
 import org.cryptomator.cryptofs.CryptoFileSystem;
 import org.cryptomator.cryptofs.CryptoFileSystemProperties;
-import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
 import org.cryptomator.cryptofs.CryptoFileSystemProvider;
 import org.cryptomator.cryptolib.api.CryptoException;
 import org.cryptomator.cryptolib.api.InvalidPassphraseException;
@@ -86,14 +84,14 @@ public class Vault {
 	// Commands
 	// ********************************************************************************/
 
-	private CryptoFileSystem getCryptoFileSystem(CharSequence passphrase) throws IOException, CryptoException {
+	private CryptoFileSystem getCryptoFileSystem(CharSequence passphrase) throws IOException, InvalidPassphraseException, CryptoException {
 		return LazyInitializer.initializeLazily(cryptoFileSystem, () -> unlockCryptoFileSystem(passphrase), IOException.class);
 	}
 
-	private CryptoFileSystem unlockCryptoFileSystem(CharSequence passphrase) throws IOException, CryptoException {
+	private CryptoFileSystem unlockCryptoFileSystem(CharSequence passphrase) throws IOException, InvalidPassphraseException, CryptoException {
 		CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
 				.withPassphrase(passphrase) //
-				.withFlags(EnumSet.noneOf(FileSystemFlags.class)) // TODO: use withFlags() with CryptoFS 1.3.1
+				.withFlags() //
 				.withMasterkeyFilename(MASTERKEY_FILENAME) //
 				.build();
 		return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
@@ -118,7 +116,7 @@ public class Vault {
 		CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase);
 	}
 
-	public synchronized void unlock(CharSequence passphrase) throws ServerLifecycleException {
+	public synchronized void unlock(CharSequence passphrase) throws InvalidPassphraseException, ServerLifecycleException {
 		try {
 			FileSystem fs = getCryptoFileSystem(passphrase);
 			if (!server.isRunning()) {

+ 3 - 0
main/ui/src/main/resources/localization/en.txt

@@ -54,6 +54,9 @@ upgrade.version4to5.title=Vault Version 4 to 5 Upgrade
 upgrade.version4to5.msg=This vault needs to be migrated to a newer format.\nEncrypted files will be updated.\nPlease make sure synchronization has finished before proceeding.\n\nNote: Modification date of all files will be changed to the current date/time in the process.
 upgrade.version4to5.err.io=Migration failed due to an I/O Exception. See log file for details.
 
+upgrade.version5toX.title=Vault Version Upgrade
+upgrade.version5toX.msg=This vault needs to be migrated to a newer format.\nPlease make sure synchronization has finished before proceeding.
+
 # unlock.fxml
 unlock.label.password=Password
 unlock.label.savePassword=Save Password