mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 02:31:27 +00:00
fixes #149
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user