Browse Source

randomizing masterkeys is now a explicit task, so random numbers are not needed when loading a masterkey file from disk. trying to use an uninitialized cryptor results in runtime exceptions

Sebastian Stenzel 9 years ago
parent
commit
a7c19624ce

+ 1 - 0
main/core/src/test/java/org/cryptomator/webdav/jackrabbit/RangeRequestTest.java

@@ -49,6 +49,7 @@ public class RangeRequestTest {
 
 	@BeforeClass
 	public static void startServer() throws URISyntaxException {
+		CRYPTOR.randomizeMasterKey();
 		SERVER.start();
 		SERVLET = SERVER.createServlet(TMP_VAULT.toPath(), CRYPTOR, new ArrayList<String>(), new ArrayList<String>(), "JUnitTestVault");
 		SERVLET.start();

+ 1 - 1
main/crypto-aes/pom.xml

@@ -39,7 +39,7 @@
 		<dependency>
 			<groupId>org.cryptomator</groupId>
 			<artifactId>siv-mode</artifactId>
-			<version>1.0.1</version>
+			<version>1.0.2</version>
 		</dependency>
 
 		<!-- Commons -->

+ 15 - 14
main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java

@@ -103,25 +103,30 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 	 * Creates a new Cryptor with a newly initialized PRNG.
 	 */
 	public Aes256Cryptor() {
-		byte[] bytes = new byte[AES_KEY_LENGTH_IN_BITS / Byte.SIZE];
 		try {
 			securePrng = SecureRandom.getInstanceStrong();
 			// No setSeed needed. See SecureRandom.getInstance(String):
 			// The first call to nextBytes will force the SecureRandom object to seed itself
+		} catch (NoSuchAlgorithmException e) {
+			throw new IllegalStateException("PRNG algorithm should exist.", e);
+		}
+	}
+
+	@Override
+	public void randomizeMasterKey() {
+		byte[] bytes = new byte[AES_KEY_LENGTH_IN_BITS / Byte.SIZE];
+		try {
+			// No setSeed needed. See SecureRandom.getInstance(String):
+			// The first call to nextBytes will force the SecureRandom object to seed itself
 			securePrng.nextBytes(bytes);
 			this.primaryMasterKey = new SecretKeySpec(bytes, AES_KEY_ALGORITHM);
 			securePrng.nextBytes(bytes);
 			this.hMacMasterKey = new SecretKeySpec(bytes, HMAC_KEY_ALGORITHM);
-		} catch (NoSuchAlgorithmException e) {
-			throw new IllegalStateException("PRNG algorithm should exist.", e);
 		} finally {
 			Arrays.fill(bytes, (byte) 0);
 		}
 	}
 
-	/**
-	 * Encrypts the current masterKey with the given password and writes the result to the given output stream.
-	 */
 	@Override
 	public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
 		try {
@@ -149,14 +154,6 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 		}
 	}
 
-	/**
-	 * Reads the encrypted masterkey from the given input stream and decrypts it with the given password.
-	 * 
-	 * @throws DecryptFailedException If the decryption failed for various reasons (including wrong password).
-	 * @throws WrongPasswordException If the provided password was wrong. Note: Sometimes the algorithm itself fails due to a wrong password. In this case a DecryptFailedException will be thrown.
-	 * @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In this case Java JCE needs to be installed.
-	 * @throws UnsupportedVaultException If the masterkey file is too old or too modern.
-	 */
 	@Override
 	public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException {
 		try {
@@ -194,6 +191,10 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
 
 	@Override
 	public boolean isDestroyed() {
+		if (primaryMasterKey == null || hMacMasterKey == null) {
+			// master keys have not been set yet, so there is nothing sensitive
+			return true;
+		}
 		return primaryMasterKey.isDestroyed() && hMacMasterKey.isDestroyed();
 	}
 

+ 8 - 0
main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java

@@ -33,6 +33,8 @@ public class Aes256CryptorTest {
 	public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException, DestroyFailedException, UnsupportedVaultException {
 		final String pw = "asd";
 		final Aes256Cryptor cryptor = new Aes256Cryptor();
+		cryptor.randomizeMasterKey();
+
 		final ByteArrayOutputStream out = new ByteArrayOutputStream();
 		cryptor.encryptMasterKey(out, pw);
 		cryptor.destroy();
@@ -49,6 +51,8 @@ public class Aes256CryptorTest {
 	public void testWrongPassword() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, DestroyFailedException, UnsupportedVaultException {
 		final String pw = "asd";
 		final Aes256Cryptor cryptor = new Aes256Cryptor();
+		cryptor.randomizeMasterKey();
+
 		final ByteArrayOutputStream out = new ByteArrayOutputStream();
 		cryptor.encryptMasterKey(out, pw);
 		cryptor.destroy();
@@ -78,6 +82,7 @@ public class Aes256CryptorTest {
 
 		// init cryptor:
 		final Aes256Cryptor cryptor = new Aes256Cryptor();
+		cryptor.randomizeMasterKey();
 
 		// encrypt:
 		final ByteBuffer encryptedData = ByteBuffer.allocate(104 + plaintextData.length + 4096);
@@ -110,6 +115,7 @@ public class Aes256CryptorTest {
 
 		// init cryptor:
 		final Aes256Cryptor cryptor = new Aes256Cryptor();
+		cryptor.randomizeMasterKey();
 
 		// encrypt:
 		final ByteBuffer encryptedData = ByteBuffer.allocate(104 + plaintextData.length + 4096 + 32); // header + content + maximum possible size obfuscation padding + 32 bytes mac (per each 32k)
@@ -149,6 +155,7 @@ public class Aes256CryptorTest {
 
 		// init cryptor:
 		final Aes256Cryptor cryptor = new Aes256Cryptor();
+		cryptor.randomizeMasterKey();
 
 		// encrypt:
 		final ByteBuffer encryptedData = ByteBuffer.allocate((int) (104 + plaintextData.length * 1.2));
@@ -176,6 +183,7 @@ public class Aes256CryptorTest {
 	@Test
 	public void testEncryptionOfFilenames() throws IOException, DecryptFailedException {
 		final Aes256Cryptor cryptor = new Aes256Cryptor();
+		cryptor.randomizeMasterKey();
 
 		// directory paths
 		final String originalPath1 = "foo/bar/baz";

+ 5 - 0
main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java

@@ -22,6 +22,11 @@ public class AbstractCryptorDecorator implements Cryptor {
 		this.cryptor = cryptor;
 	}
 
+	@Override
+	public void randomizeMasterKey() {
+		cryptor.randomizeMasterKey();
+	}
+
 	@Override
 	public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
 		cryptor.encryptMasterKey(out, password);

+ 5 - 0
main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java

@@ -27,6 +27,11 @@ import org.cryptomator.crypto.exceptions.WrongPasswordException;
  */
 public interface Cryptor extends Destroyable {
 
+	/**
+	 * Assigns new random bytes to the keys in this Cryptor instance.
+	 */
+	void randomizeMasterKey();
+
 	/**
 	 * Encrypts the current masterKey with the given password and writes the result to the given output stream.
 	 */

+ 1 - 0
main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java

@@ -93,6 +93,7 @@ public class InitializeController extends AbstractFXMLViewController {
 		final Path masterKeyPath = vault.getPath().resolve(Vault.VAULT_MASTERKEY_FILE);
 		final CharSequence password = passwordField.getCharacters();
 		try (OutputStream masterKeyOutputStream = Files.newOutputStream(masterKeyPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)) {
+			vault.getCryptor().randomizeMasterKey();
 			vault.getCryptor().encryptMasterKey(masterKeyOutputStream, password);
 			final String dataRootDir = vault.getCryptor().encryptDirectoryPath("", FileSystems.getDefault().getSeparator());
 			final Path dataRootPath = vault.getPath().resolve("d").resolve(dataRootDir);