Sebastian Stenzel преди 9 години
родител
ревизия
c130d0e4a0

+ 16 - 2
main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java

@@ -11,17 +11,21 @@ package org.cryptomator.crypto.engine.impl;
 import static org.cryptomator.crypto.engine.impl.Constants.CURRENT_VAULT_VERSION;
 
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.security.InvalidKeyException;
+import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicReference;
 
+import javax.crypto.Mac;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 import javax.security.auth.DestroyFailedException;
 import javax.security.auth.Destroyable;
 
+import org.apache.commons.lang3.ArrayUtils;
 import org.cryptomator.common.LazyInitializer;
 import org.cryptomator.crypto.engine.Cryptor;
 import org.cryptomator.crypto.engine.FileContentCryptor;
@@ -100,15 +104,21 @@ class CryptorImpl implements Cryptor {
 		}
 
 		// check version
-		if (keyFile.getVersion() != CURRENT_VAULT_VERSION) {
+		if (keyFile.getVersion() != CURRENT_VAULT_VERSION || ArrayUtils.isEmpty(keyFile.getVersionMac())) {
 			throw new UnsupportedVaultFormatException(keyFile.getVersion(), CURRENT_VAULT_VERSION);
 		}
 
 		final byte[] kekBytes = Scrypt.scrypt(passphrase, keyFile.getScryptSalt(), keyFile.getScryptCostParam(), keyFile.getScryptBlockSize(), KEYLENGTH_IN_BYTES);
 		try {
 			final SecretKey kek = new SecretKeySpec(kekBytes, ENCRYPTION_ALG);
-			this.encryptionKey = AesKeyWrap.unwrap(kek, keyFile.getEncryptionMasterKey(), ENCRYPTION_ALG);
 			this.macKey = AesKeyWrap.unwrap(kek, keyFile.getMacMasterKey(), MAC_ALG);
+			final Mac mac = new ThreadLocalMac(macKey, MAC_ALG).get();
+			final byte[] versionMac = mac.doFinal(ByteBuffer.allocate(Integer.BYTES).putInt(CURRENT_VAULT_VERSION).array());
+			if (!MessageDigest.isEqual(versionMac, keyFile.getVersionMac())) {
+				destroyQuietly(macKey);
+				throw new UnsupportedVaultFormatException(Integer.MAX_VALUE, CURRENT_VAULT_VERSION);
+			}
+			this.encryptionKey = AesKeyWrap.unwrap(kek, keyFile.getEncryptionMasterKey(), ENCRYPTION_ALG);
 		} catch (InvalidKeyException e) {
 			throw new InvalidPassphraseException();
 		} catch (NoSuchAlgorithmException e) {
@@ -134,6 +144,9 @@ class CryptorImpl implements Cryptor {
 			Arrays.fill(kekBytes, (byte) 0x00);
 		}
 
+		final Mac mac = new ThreadLocalMac(macKey, MAC_ALG).get();
+		final byte[] versionMac = mac.doFinal(ByteBuffer.allocate(Integer.BYTES).putInt(CURRENT_VAULT_VERSION).array());
+
 		final KeyFile keyfile = new KeyFile();
 		keyfile.setVersion(CURRENT_VAULT_VERSION);
 		keyfile.setScryptSalt(scryptSalt);
@@ -141,6 +154,7 @@ class CryptorImpl implements Cryptor {
 		keyfile.setScryptBlockSize(SCRYPT_BLOCK_SIZE);
 		keyfile.setEncryptionMasterKey(wrappedEncryptionKey);
 		keyfile.setMacMasterKey(wrappedMacKey);
+		keyfile.setVersionMac(versionMac);
 
 		try {
 			final ObjectMapper om = new ObjectMapper();

+ 12 - 1
main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/KeyFile.java

@@ -15,7 +15,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 
 @JsonIgnoreProperties(ignoreUnknown = true)
-@JsonPropertyOrder(value = {"version", "scryptSalt", "scryptCostParam", "scryptBlockSize", "primaryMasterKey", "hmacMasterKey"})
+@JsonPropertyOrder(value = {"version", "scryptSalt", "scryptCostParam", "scryptBlockSize", "primaryMasterKey", "hmacMasterKey", "versionMac"})
 class KeyFile implements Serializable {
 
 	private static final long serialVersionUID = 8578363158959619885L;
@@ -38,6 +38,9 @@ class KeyFile implements Serializable {
 	@JsonProperty("hmacMasterKey")
 	private byte[] macMasterKey;
 
+	@JsonProperty("versionMac")
+	private byte[] versionMac;
+
 	public Integer getVersion() {
 		return version;
 	}
@@ -86,4 +89,12 @@ class KeyFile implements Serializable {
 		this.macMasterKey = macMasterKey;
 	}
 
+	public byte[] getVersionMac() {
+		return versionMac;
+	}
+
+	public void setVersionMac(byte[] versionMac) {
+		this.versionMac = versionMac;
+	}
+
 }

+ 27 - 4
main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptorImplTest.java

@@ -22,7 +22,8 @@ public class CryptorImplTest {
 	public void testMasterkeyDecryptionWithCorrectPassphrase() throws IOException {
 		final String testMasterKey = "{\"version\":3,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":2,\"scryptBlockSize\":8," //
 				+ "\"primaryMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
-				+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"}";
+				+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
+				+ "\"versionMac\":\"iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=\"}";
 		final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
 		cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "asd");
 	}
@@ -31,7 +32,8 @@ public class CryptorImplTest {
 	public void testMasterkeyDecryptionWithWrongPassphrase() throws IOException {
 		final String testMasterKey = "{\"version\":3,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":2,\"scryptBlockSize\":8," //
 				+ "\"primaryMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
-				+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"}";
+				+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
+				+ "\"versionMac\":\"iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=\"}";
 		final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
 		cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "qwe");
 	}
@@ -39,17 +41,38 @@ public class CryptorImplTest {
 	@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==\"," //
+				+ "\"versionMac\":\"iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=\"}";
+		final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
+		cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "asd");
+	}
+
+	@Test(expected = UnsupportedVaultFormatException.class)
+	public void testMasterkeyDecryptionWithMissingVersionMac() throws IOException {
+		final String testMasterKey = "{\"version\":3,\"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");
+		cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "asd");
+	}
+
+	@Test(expected = UnsupportedVaultFormatException.class)
+	public void testMasterkeyDecryptionWithWrongVersionMac() throws IOException {
+		final String testMasterKey = "{\"version\":3,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":2,\"scryptBlockSize\":8," //
+				+ "\"primaryMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
+				+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
+				+ "\"versionMac\":\"iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLa=\"}";
+		final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
+		cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "asd");
 	}
 
 	@Test
 	public void testMasterkeyEncryption() throws IOException {
 		final String expectedMasterKey = "{\"version\":3,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":16384,\"scryptBlockSize\":8," //
 				+ "\"primaryMasterKey\":\"BJPIq5pvhN24iDtPJLMFPLaVJWdGog9k4n0P03j4ru+ivbWY9OaRGQ==\"," //
-				+ "\"hmacMasterKey\":\"BJPIq5pvhN24iDtPJLMFPLaVJWdGog9k4n0P03j4ru+ivbWY9OaRGQ==\"}";
+				+ "\"hmacMasterKey\":\"BJPIq5pvhN24iDtPJLMFPLaVJWdGog9k4n0P03j4ru+ivbWY9OaRGQ==\"," //
+				+ "\"versionMac\":\"iUmRRHITuyJsJbVNqGNw+82YQ4A3Rma7j/y1v0DCVLA=\"}";
 		final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
 		cryptor.randomizeMasterkey();
 		final byte[] masterkeyFile = cryptor.writeKeysToMasterkeyFile("asd");