Fixed performance implications due to slow /dev/random. Now seeding PRNG only once per Cryptor. Fixes #36

This commit is contained in:
Sebastian Stenzel 2015-02-23 14:51:52 +01:00
parent cba8bbefc5
commit 63f64fae03
8 changed files with 28 additions and 51 deletions

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.5.0</version>
<version>0.5.1</version>
</parent>
<artifactId>core</artifactId>
<name>Cryptomator WebDAV and I/O module</name>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.5.0</version>
<version>0.5.1</version>
</parent>
<artifactId>crypto-aes</artifactId>
<name>Cryptomator cryptographic module (AES)</name>

View File

@ -25,7 +25,6 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import javax.crypto.BadPaddingException;
@ -58,19 +57,19 @@ import com.fasterxml.jackson.databind.ObjectMapper;
public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicConfiguration, FileNamingConventions {
/**
* PRNG for cryptographically secure random numbers. Defaults to SHA1-based number generator.
*
* @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SecureRandom
*/
private static final SecureRandom SECURE_PRNG;
/**
* Defined in static initializer. Defaults to 256, but falls back to maximum value possible, if JCE Unlimited Strength Jurisdiction
* Policy Files isn't installed. Those files can be downloaded here: http://www.oracle.com/technetwork/java/javase/downloads/.
*/
private static final int AES_KEY_LENGTH_IN_BITS;
/**
* PRNG for cryptographically secure random numbers. Defaults to SHA1-based number generator.
*
* @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SecureRandom
*/
private final SecureRandom securePrng;
/**
* Jackson JSON-Mapper.
*/
@ -89,7 +88,6 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
static {
try {
SECURE_PRNG = SecureRandom.getInstance(PRNG_ALGORITHM);
final int maxKeyLength = Cipher.getMaxAllowedKeyLength(AES_KEY_ALGORITHM);
AES_KEY_LENGTH_IN_BITS = (maxKeyLength >= PREF_MASTER_KEY_LENGTH_IN_BITS) ? PREF_MASTER_KEY_LENGTH_IN_BITS : maxKeyLength;
} catch (NoSuchAlgorithmException e) {
@ -101,33 +99,16 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
* Creates a new Cryptor with a newly initialized PRNG.
*/
public Aes256Cryptor() {
SECURE_PRNG.setSeed(SECURE_PRNG.generateSeed(PRNG_SEED_LENGTH));
byte[] bytes = new byte[AES_KEY_LENGTH_IN_BITS / Byte.SIZE];
try {
SECURE_PRNG.nextBytes(bytes);
securePrng = SecureRandom.getInstance(PRNG_ALGORITHM);
securePrng.setSeed(securePrng.generateSeed(PRNG_SEED_LENGTH));
securePrng.nextBytes(bytes);
this.primaryMasterKey = new SecretKeySpec(bytes, AES_KEY_ALGORITHM);
SECURE_PRNG.nextBytes(bytes);
this.hMacMasterKey = new SecretKeySpec(bytes, HMAC_KEY_ALGORITHM);
} finally {
Arrays.fill(bytes, (byte) 0);
}
}
/**
* Creates a new Cryptor with the given PRNG.<br/>
* <strong>DO NOT USE IN PRODUCTION</strong>. This constructor must only be used in in unit tests. Do not change method visibility.
*
* @param prng Fast, possibly insecure PRNG.
*/
Aes256Cryptor(Random prng) {
byte[] bytes = new byte[AES_KEY_LENGTH_IN_BITS / Byte.SIZE];
try {
prng.nextBytes(bytes);
this.primaryMasterKey = new SecretKeySpec(bytes, AES_KEY_ALGORITHM);
prng.nextBytes(bytes);
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);
}
@ -266,8 +247,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
private byte[] randomData(int length) {
final byte[] result = new byte[length];
SECURE_PRNG.setSeed(SECURE_PRNG.generateSeed(PRNG_SEED_LENGTH));
SECURE_PRNG.nextBytes(result);
securePrng.nextBytes(result);
return result;
}

View File

@ -33,7 +33,7 @@ interface AesCryptographicConfiguration {
/**
* Number of bytes used as seed for the PRNG.
*/
int PRNG_SEED_LENGTH = 16;
int PRNG_SEED_LENGTH = 32;
/**
* Algorithm used for random number generation.

View File

@ -17,7 +17,6 @@ import java.nio.channels.SeekableByteChannel;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.apache.commons.io.IOUtils;
import org.cryptomator.crypto.CryptorIOSupport;
@ -29,17 +28,15 @@ import org.junit.Test;
public class Aes256CryptorTest {
private static final Random TEST_PRNG = new Random();
@Test
public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor cryptor = new Aes256Cryptor();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
final Aes256Cryptor decryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor decryptor = new Aes256Cryptor();
final InputStream in = new ByteArrayInputStream(out.toByteArray());
decryptor.decryptMasterKey(in, pw);
@ -50,7 +47,7 @@ public class Aes256CryptorTest {
@Test
public void testWrongPassword() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor cryptor = new Aes256Cryptor();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@ -58,7 +55,7 @@ public class Aes256CryptorTest {
// all these passwords are expected to fail.
final String[] wrongPws = {"a", "as", "asdf", "sdf", "das", "dsa", "foo", "bar", "baz"};
final Aes256Cryptor decryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor decryptor = new Aes256Cryptor();
for (final String wrongPw : wrongPws) {
final InputStream in = new ByteArrayInputStream(out.toByteArray());
try {
@ -79,7 +76,7 @@ public class Aes256CryptorTest {
final InputStream plaintextIn = new ByteArrayInputStream(plaintextData);
// init cryptor:
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor cryptor = new Aes256Cryptor();
// encrypt:
final ByteBuffer encryptedData = ByteBuffer.allocate(96);
@ -111,7 +108,7 @@ public class Aes256CryptorTest {
final InputStream plaintextIn = new ByteArrayInputStream(plaintextData);
// init cryptor:
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor cryptor = new Aes256Cryptor();
// encrypt:
final ByteBuffer encryptedData = ByteBuffer.allocate(96);
@ -150,7 +147,7 @@ public class Aes256CryptorTest {
final InputStream plaintextIn = new ByteArrayInputStream(plaintextData);
// init cryptor:
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor cryptor = new Aes256Cryptor();
// encrypt:
final ByteBuffer encryptedData = ByteBuffer.allocate((int) (64 + plaintextData.length * 1.2));
@ -178,7 +175,7 @@ public class Aes256CryptorTest {
@Test
public void testEncryptionOfFilenames() throws IOException, DecryptFailedException {
final CryptorIOSupport ioSupportMock = new CryptoIOSupportMock();
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final Aes256Cryptor cryptor = new Aes256Cryptor();
// short path components
final String originalPath1 = "foo/bar/baz";

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.5.0</version>
<version>0.5.1</version>
</parent>
<artifactId>crypto-api</artifactId>
<name>Cryptomator cryptographic module API</name>

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.5.0</version>
<version>0.5.1</version>
<packaging>pom</packaging>
<name>Cryptomator</name>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.5.0</version>
<version>0.5.1</version>
</parent>
<artifactId>ui</artifactId>
<name>Cryptomator GUI</name>