check vault version before unlocking

This commit is contained in:
Sebastian Stenzel
2016-02-28 18:14:07 +01:00
parent cbb669aa40
commit bc9b7c3a19
11 changed files with 94 additions and 25 deletions

View File

@@ -21,7 +21,7 @@ public interface Cryptor extends Destroyable {
void randomizeMasterkey();
void readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) throws InvalidPassphraseException;
void readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) throws InvalidPassphraseException, UnsupportedVaultFormatException;
byte[] writeKeysToMasterkeyFile(CharSequence passphrase);

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.crypto.engine;
public class UnsupportedVaultFormatException extends CryptoException {
private final Integer detectedVersion;
private final Integer supportedVersion;
public UnsupportedVaultFormatException(Integer detectedVersion, Integer supportedVersion) {
super("Tried to open vault of version " + detectedVersion + ", but can only handle version " + supportedVersion);
this.detectedVersion = detectedVersion;
this.supportedVersion = supportedVersion;
}
public Integer getDetectedVersion() {
return detectedVersion;
}
public Integer getSupportedVersion() {
return supportedVersion;
}
public boolean isVaultOlderThanSoftware() {
return detectedVersion == null || detectedVersion < supportedVersion;
}
public boolean isSoftwareOlderThanVault() {
return detectedVersion > supportedVersion;
}
}

View File

@@ -5,6 +5,8 @@ public final class Constants {
private Constants() {
}
static final Integer CURRENT_VAULT_VERSION = 3;
public static final int PAYLOAD_SIZE = 32 * 1024;
public static final int NONCE_SIZE = 16;
public static final int MAC_SIZE = 32;

View File

@@ -8,6 +8,8 @@
*******************************************************************************/
package org.cryptomator.crypto.engine.impl;
import static org.cryptomator.crypto.engine.impl.Constants.CURRENT_VAULT_VERSION;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -25,6 +27,7 @@ import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.FileContentCryptor;
import org.cryptomator.crypto.engine.FilenameCryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -97,10 +100,8 @@ class CryptorImpl implements Cryptor {
}
// check version
if (keyFile.getVersion() != KeyFile.CURRENT_VERSION) {
// TODO
// throw new UnsupportedVaultException(keyfile.getVersion(), KeyFile.CURRENT_VERSION);
throw new IllegalArgumentException("Unsupported key (expected version: " + KeyFile.CURRENT_VERSION + ", actual version: " + keyFile.getVersion() + ")");
if (keyFile.getVersion() != CURRENT_VAULT_VERSION) {
throw new UnsupportedVaultFormatException(keyFile.getVersion(), CURRENT_VAULT_VERSION);
}
final byte[] kekBytes = Scrypt.scrypt(passphrase, keyFile.getScryptSalt(), keyFile.getScryptCostParam(), keyFile.getScryptBlockSize(), KEYLENGTH_IN_BYTES);
@@ -134,7 +135,7 @@ class CryptorImpl implements Cryptor {
}
final KeyFile keyfile = new KeyFile();
keyfile.setVersion(KeyFile.CURRENT_VERSION);
keyfile.setVersion(CURRENT_VAULT_VERSION);
keyfile.setScryptSalt(scryptSalt);
keyfile.setScryptCostParam(SCRYPT_COST_PARAM);
keyfile.setScryptBlockSize(SCRYPT_BLOCK_SIZE);

View File

@@ -10,13 +10,14 @@ package org.cryptomator.crypto.engine.impl;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder(value = {"version", "scryptSalt", "scryptCostParam", "scryptBlockSize", "primaryMasterKey", "hmacMasterKey"})
class KeyFile implements Serializable {
static final Integer CURRENT_VERSION = 3;
private static final long serialVersionUID = 8578363158959619885L;
@JsonProperty("version")

View File

@@ -0,0 +1,16 @@
package org.cryptomator.filesystem.crypto;
public final class Constants {
private Constants() {
}
static final String DATA_ROOT_DIR = "d";
static final String ROOT_DIRECOTRY_ID = "";
static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup";
static final String DIR_SUFFIX = "_";
}

View File

@@ -8,6 +8,9 @@
*******************************************************************************/
package org.cryptomator.filesystem.crypto;
import static org.cryptomator.filesystem.crypto.Constants.DATA_ROOT_DIR;
import static org.cryptomator.filesystem.crypto.Constants.ROOT_DIRECOTRY_ID;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Optional;
@@ -20,9 +23,6 @@ import org.cryptomator.filesystem.Folder;
class CryptoFileSystem extends CryptoFolder implements FileSystem {
private static final String DATA_ROOT_DIR = "d";
private static final String ROOT_DIRECOTRY_ID = "";
private final Folder physicalRoot;
private final CryptoFileSystemDelegate delegate;

View File

@@ -9,6 +9,7 @@
package org.cryptomator.filesystem.crypto;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.cryptomator.filesystem.crypto.Constants.DIR_SUFFIX;
import java.io.FileNotFoundException;
import java.io.UncheckedIOException;
@@ -31,8 +32,6 @@ import org.cryptomator.io.FileContents;
class CryptoFolder extends CryptoNode implements Folder {
static final String DIR_SUFFIX = "_";
private final WeakValuedCache<String, CryptoFolder> folders = WeakValuedCache.usingLoader(this::newFolder);
private final WeakValuedCache<String, CryptoFile> files = WeakValuedCache.usingLoader(this::newFile);
private final AtomicReference<String> directoryId = new AtomicReference<>();
@@ -114,7 +113,7 @@ class CryptoFolder extends CryptoNode implements Folder {
private String decryptChildFolderName(String encryptedFolderName) {
final byte[] dirId = getDirectoryId().get().getBytes(UTF_8);
final String ciphertext = StringUtils.removeEnd(encryptedFolderName, CryptoFolder.DIR_SUFFIX);
final String ciphertext = StringUtils.removeEnd(encryptedFolderName, DIR_SUFFIX);
return cryptor.getFilenameCryptor().decryptFilename(ciphertext, dirId);
}

View File

@@ -8,6 +8,9 @@
*******************************************************************************/
package org.cryptomator.filesystem.crypto;
import static org.cryptomator.filesystem.crypto.Constants.MASTERKEY_BACKUP_FILENAME;
import static org.cryptomator.filesystem.crypto.Constants.MASTERKEY_FILENAME;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
@@ -28,9 +31,6 @@ import org.cryptomator.filesystem.WritableFile;
@Singleton
class Masterkeys {
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
private static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup";
private final Provider<Cryptor> cryptorProvider;
@Inject

View File

@@ -12,6 +12,7 @@ import java.io.IOException;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
import org.junit.Assert;
import org.junit.Test;
@@ -35,6 +36,15 @@ public class CryptorImplTest {
cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "qwe");
}
@Test(expected = UnsupportedVaultFormatException.class)
public void testMasterkeyDecryptionWithWrongVaultFormat() throws IOException {
final String testMasterKey = "{\"version\":-1,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":2,\"scryptBlockSize\":8," //
+ "\"primaryMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"}";
final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "qwe");
}
@Test
public void testMasterkeyEncryption() throws IOException {
final String expectedMasterKey = "{\"version\":3,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":16384,\"scryptBlockSize\":8," //

View File

@@ -20,6 +20,7 @@ import javax.inject.Inject;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
import org.cryptomator.frontend.CommandFailedException;
import org.cryptomator.frontend.FrontendCreationFailedException;
import org.cryptomator.frontend.webdav.mount.WindowsDriveLetters;
@@ -261,20 +262,21 @@ public class UnlockController extends AbstractFXMLViewController {
progressIndicator.setVisible(false);
messageText.setText(resourceBundle.getString("unlock.errorMessage.wrongPassword"));
Platform.runLater(passwordField::requestFocus);
} catch (UnsupportedVaultFormatException e) {
setControlsDisabled(false);
progressIndicator.setVisible(false);
downloadsPageLink.setVisible(true);
LOG.warn("Unable to unlock vault: " + e.getMessage());
if (e.isVaultOlderThanSoftware()) {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
} else if (e.isSoftwareOlderThanVault()) {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
}
} catch (FrontendCreationFailedException ex) {
setControlsDisabled(false);
progressIndicator.setVisible(false);
messageText.setText(resourceBundle.getString("unlock.errorMessage.decryptionFailed"));
LOG.error("Decryption failed for technical reasons.", ex);
// } catch (UnsupportedVaultException e) {
// setControlsDisabled(false);
// progressIndicator.setVisible(false);
// downloadsPageLink.setVisible(true);
// if (e.isVaultOlderThanSoftware()) {
// messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
// } else if (e.isSoftwareOlderThanVault()) {
// messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
// }
} finally {
passwordField.swipe();
}