mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-20 01:26:52 -04:00
refactored migration (using cryptolib)
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<!-- dependency versions -->
|
||||
<cryptolib.version>1.0.0-SNAPSHOT</cryptolib.version>
|
||||
<log4j.version>2.1</log4j.version>
|
||||
<slf4j.version>1.7.7</slf4j.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
@@ -123,6 +124,13 @@
|
||||
<artifactId>ui</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Libs -->
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptolib</artifactId>
|
||||
<version>${cryptolib.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Logging -->
|
||||
<dependency>
|
||||
|
||||
@@ -55,6 +55,12 @@
|
||||
<artifactId>frontend-webdav</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- CryptoLib -->
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptolib</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- EasyBind -->
|
||||
<dependency>
|
||||
<groupId>org.fxmisc.easybind</groupId>
|
||||
|
||||
@@ -6,11 +6,11 @@ import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.cryptomator.crypto.engine.Cryptor;
|
||||
import org.cryptomator.crypto.engine.InvalidPassphraseException;
|
||||
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.cryptolib.api.Cryptor;
|
||||
import org.cryptomator.cryptolib.api.CryptorProvider;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.api.KeyFile;
|
||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.filesystem.crypto.Constants;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.slf4j.Logger;
|
||||
@@ -20,12 +20,16 @@ public abstract class UpgradeStrategy {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UpgradeStrategy.class);
|
||||
|
||||
protected final Provider<Cryptor> cryptorProvider;
|
||||
protected final CryptorProvider cryptorProvider;
|
||||
protected final Localization localization;
|
||||
protected final int vaultVersionBeforeUpgrade;
|
||||
protected final int vaultVersionAfterUpgrade;
|
||||
|
||||
UpgradeStrategy(Provider<Cryptor> cryptorProvider, Localization localization) {
|
||||
UpgradeStrategy(CryptorProvider cryptorProvider, Localization localization, int vaultVersionBeforeUpgrade, int vaultVersionAfterUpgrade) {
|
||||
this.cryptorProvider = cryptorProvider;
|
||||
this.localization = localization;
|
||||
this.vaultVersionBeforeUpgrade = vaultVersionBeforeUpgrade;
|
||||
this.vaultVersionAfterUpgrade = vaultVersionAfterUpgrade;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,27 +41,29 @@ public abstract class UpgradeStrategy {
|
||||
* Upgrades a vault. Might take a moment, should be run in a background thread.
|
||||
*/
|
||||
public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException {
|
||||
final Cryptor cryptor = cryptorProvider.get();
|
||||
Cryptor cryptor = null;
|
||||
try {
|
||||
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
|
||||
final byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
|
||||
cryptor.readKeysFromMasterkeyFile(masterkeyFileContents, passphrase);
|
||||
cryptor = cryptorProvider.createFromKeyFile(KeyFile.parse(masterkeyFileContents), passphrase, vaultVersionBeforeUpgrade);
|
||||
// create backup, as soon as we know the password was correct:
|
||||
final Path masterkeyBackupFile = vault.path().getValue().resolve(Constants.MASTERKEY_BACKUP_FILENAME);
|
||||
Files.copy(masterkeyFile, masterkeyBackupFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
// do stuff:
|
||||
upgrade(vault, cryptor);
|
||||
// write updated masterkey file:
|
||||
final byte[] upgradedMasterkeyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase);
|
||||
final Path masterkeyFileAfterUpgrading = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); // path may have changed
|
||||
Files.write(masterkeyFileAfterUpgrading, upgradedMasterkeyFileContents, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
final byte[] upgradedMasterkeyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase, vaultVersionAfterUpgrade).serialize();
|
||||
final Path masterkeyFileAfterUpgrade = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); // path may have changed
|
||||
Files.write(masterkeyFileAfterUpgrade, upgradedMasterkeyFileContents, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
} catch (InvalidPassphraseException e) {
|
||||
throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword"));
|
||||
} catch (IOException | UnsupportedVaultFormatException e) {
|
||||
LOG.warn("Upgrade failed.", e);
|
||||
throw new UpgradeFailedException("Upgrade failed. Details in log message.");
|
||||
} finally {
|
||||
cryptor.destroy();
|
||||
if (cryptor != null) {
|
||||
cryptor.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +74,21 @@ public abstract class UpgradeStrategy {
|
||||
*
|
||||
* @return <code>true</code> if and only if the vault can be migrated to a newer version without the risk of data losses.
|
||||
*/
|
||||
public abstract boolean isApplicable(Vault vault);
|
||||
public boolean isApplicable(Vault vault) {
|
||||
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
|
||||
try {
|
||||
if (Files.isRegularFile(masterkeyFile)) {
|
||||
byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
|
||||
return KeyFile.parse(masterkeyFileContents).getVersion() == vaultVersionBeforeUpgrade;
|
||||
} else {
|
||||
LOG.warn("Not a file: {}", masterkeyFile);
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Could not determine, whether upgrade is applicable.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when data migration failed.
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cryptomator.crypto.engine.Cryptor;
|
||||
import org.cryptomator.crypto.engine.InvalidPassphraseException;
|
||||
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
|
||||
import org.cryptomator.filesystem.crypto.Constants;
|
||||
import org.cryptomator.cryptolib.Cryptors;
|
||||
import org.cryptomator.cryptolib.api.Cryptor;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.slf4j.Logger;
|
||||
@@ -28,8 +25,8 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
private final Settings settings;
|
||||
|
||||
@Inject
|
||||
public UpgradeVersion3DropBundleExtension(Provider<Cryptor> cryptorProvider, Localization localization, Settings settings) {
|
||||
super(cryptorProvider, localization);
|
||||
public UpgradeVersion3DropBundleExtension(SecureRandom secureRandom, Localization localization, Settings settings) {
|
||||
super(Cryptors.version1(secureRandom), localization, 3, 3);
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@@ -42,25 +39,6 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
return String.format(fmt, oldVaultName, newVaultName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException {
|
||||
final Cryptor cryptor = cryptorProvider.get();
|
||||
try {
|
||||
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
|
||||
final byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile);
|
||||
cryptor.readKeysFromMasterkeyFile(masterkeyFileContents, passphrase);
|
||||
upgrade(vault, cryptor);
|
||||
// don't write new masterkey. this is a special case, as we were stupid and didn't increase the vault version with this upgrade...
|
||||
} catch (InvalidPassphraseException e) {
|
||||
throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword"));
|
||||
} catch (IOException | UnsupportedVaultFormatException e) {
|
||||
LOG.warn("Upgrade failed.", e);
|
||||
throw new UpgradeFailedException("Upgrade failed. Details in log message.");
|
||||
} finally {
|
||||
cryptor.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
|
||||
Path path = vault.path().getValue();
|
||||
@@ -73,6 +51,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
throw new UpgradeFailedException(msg);
|
||||
} else {
|
||||
try {
|
||||
LOG.info("Renaming {} to {}", path, newPath.getFileName());
|
||||
Files.move(path, path.resolveSibling(newVaultName));
|
||||
Platform.runLater(() -> {
|
||||
vault.setPath(newPath);
|
||||
@@ -89,19 +68,7 @@ class UpgradeVersion3DropBundleExtension extends UpgradeStrategy {
|
||||
public boolean isApplicable(Vault vault) {
|
||||
Path vaultPath = vault.path().getValue();
|
||||
if (vaultPath.toString().endsWith(Vault.VAULT_FILE_EXTENSION)) {
|
||||
final Path masterkeyFile = vaultPath.resolve(Constants.MASTERKEY_FILENAME);
|
||||
try {
|
||||
if (Files.isRegularFile(masterkeyFile)) {
|
||||
final String keyContents = new String(Files.readAllBytes(masterkeyFile), StandardCharsets.UTF_8);
|
||||
return keyContents.contains("\"version\":3") || keyContents.contains("\"version\": 3");
|
||||
} else {
|
||||
LOG.warn("Not a file: {}", masterkeyFile);
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Could not determine, whether upgrade is applicable.", e);
|
||||
return false;
|
||||
}
|
||||
return super.isApplicable(vault);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -9,19 +9,19 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.codec.binary.Base32;
|
||||
import org.apache.commons.codec.binary.BaseNCodec;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cryptomator.crypto.engine.Cryptor;
|
||||
import org.cryptomator.filesystem.crypto.Constants;
|
||||
import org.cryptomator.cryptolib.Cryptors;
|
||||
import org.cryptomator.cryptolib.api.Cryptor;
|
||||
import org.cryptomator.cryptolib.common.MessageDigestSupplier;
|
||||
import org.cryptomator.ui.settings.Localization;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -40,17 +40,12 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
|
||||
private static final String OLD_FOLDER_SUFFIX = "_";
|
||||
private static final String NEW_FOLDER_PREFIX = "0";
|
||||
|
||||
private final MessageDigest sha1;
|
||||
private final MessageDigest sha1 = MessageDigestSupplier.SHA1.get();
|
||||
private final BaseNCodec base32 = new Base32();
|
||||
|
||||
@Inject
|
||||
public UpgradeVersion3to4(Provider<Cryptor> cryptorProvider, Localization localization) {
|
||||
super(cryptorProvider, localization);
|
||||
try {
|
||||
sha1 = MessageDigest.getInstance("SHA-1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError("SHA-1 exists in every JVM");
|
||||
}
|
||||
public UpgradeVersion3to4(SecureRandom secureRandom, Localization localization) {
|
||||
super(Cryptors.version1(secureRandom), localization, 3, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,21 +139,4 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Vault vault) {
|
||||
final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
|
||||
try {
|
||||
if (Files.isRegularFile(masterkeyFile)) {
|
||||
final String keyContents = new String(Files.readAllBytes(masterkeyFile), UTF_8);
|
||||
return keyContents.contains("\"version\":3") || keyContents.contains("\"version\": 3");
|
||||
} else {
|
||||
LOG.warn("Not a file: {}", masterkeyFile);
|
||||
return false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Could not determine, whether upgrade is applicable.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user