diff --git a/main/core/src/test/java/org/cryptomator/webdav/jackrabbit/RangeRequestTest.java b/main/core/src/test/java/org/cryptomator/webdav/jackrabbit/RangeRequestTest.java index 9125cba8e..5c84d910d 100644 --- a/main/core/src/test/java/org/cryptomator/webdav/jackrabbit/RangeRequestTest.java +++ b/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(), new ArrayList(), "JUnitTestVault"); SERVLET.start(); diff --git a/main/crypto-aes/pom.xml b/main/crypto-aes/pom.xml index e9ebfa724..491e9446f 100644 --- a/main/crypto-aes/pom.xml +++ b/main/crypto-aes/pom.xml @@ -39,7 +39,7 @@ org.cryptomator siv-mode - 1.0.1 + 1.0.2 diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java index 73c18c501..c091458db 100644 --- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java +++ b/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(); } diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java index 2cb1691d0..34a8c2669 100644 --- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java +++ b/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"; diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java index 5767a717d..0db336ec5 100644 --- a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java +++ b/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); diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java index 720ee011c..a17f26238 100644 --- a/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java +++ b/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. */ diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java index ff1488e1e..cdb297f1d 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java +++ b/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);