Skip to content

Commit

Permalink
refactored migration (using cryptolib)
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Aug 23, 2016
1 parent a499a3c commit 8c8db84
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 83 deletions.
8 changes: 8 additions & 0 deletions main/pom.xml
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
6 changes: 6 additions & 0 deletions main/ui/pom.xml
Expand Up @@ -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>
Expand Down
48 changes: 34 additions & 14 deletions main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java
Expand Up @@ -6,11 +6,11 @@
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;
Expand All @@ -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;
}

/**
Expand All @@ -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();
}
}
}

Expand All @@ -68,7 +74,21 @@ public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedEx
*
* @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.
Expand Down
@@ -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;
Expand All @@ -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;
}

Expand All @@ -42,25 +39,6 @@ public String getNotification(Vault vault) {
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();
Expand All @@ -73,6 +51,7 @@ protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedExcepti
throw new UpgradeFailedException(msg);
} else {
try {
LOG.info("Renaming {} to {}", path, newPath.getFileName());
Files.move(path, path.resolveSibling(newVaultName));
Platform.runLater(() -> {
vault.setPath(newPath);
Expand All @@ -89,19 +68,7 @@ protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedExcepti
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;
}
Expand Down
Expand Up @@ -9,19 +9,19 @@
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;
Expand All @@ -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
Expand Down Expand Up @@ -144,21 +139,4 @@ private void migrateLong(Path metadataDir, Path path) throws IOException {
}
}

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

}

0 comments on commit 8c8db84

Please sign in to comment.