Skip to content

Commit

Permalink
make some better use of mocks during unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Stenzel committed Dec 7, 2015
1 parent 0697e19 commit 97a72ec
Show file tree
Hide file tree
Showing 15 changed files with 252 additions and 28 deletions.
5 changes: 0 additions & 5 deletions main/core/pom.xml
Expand Up @@ -27,11 +27,6 @@
<groupId>org.cryptomator</groupId>
<artifactId>crypto-api</artifactId>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>crypto-aes</artifactId>
<scope>test</scope>
</dependency>

<!-- Jetty (Servlet Container) -->
<dependency>
Expand Down
@@ -0,0 +1,103 @@
package org.cryptomator.webdav.jackrabbit;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;

import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.EncryptFailedException;
import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
import org.cryptomator.crypto.exceptions.WrongPasswordException;

class CryptorMock implements Cryptor {

private static final int BUFSIZE = 32768;

@Override
public void randomizeMasterKey() {
// noop
}

@Override
public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
// noop
}

@Override
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException {
// noop
}

@Override
public String encryptDirectoryPath(String cleartextDirectoryId, String nativePathSep) {
return cleartextDirectoryId;
}

@Override
public String encryptFilename(String cleartextName) {
return cleartextName;
}

@Override
public String decryptFilename(String ciphertextName) throws DecryptFailedException {
return ciphertextName;
}

@Override
public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException, MacAuthenticationFailedException {
return encryptedFile.size();
}

@Override
public Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile, boolean authenticate) throws IOException, DecryptFailedException {
ByteBuffer buf = ByteBuffer.allocate(BUFSIZE);
long numReadTotal = 0;
int numRead = 0;
while ((numRead = encryptedFile.read(buf)) != -1) {
numReadTotal += numRead;
buf.flip();
byte[] bytes = new byte[numRead];
buf.get(bytes);
plaintextFile.write(bytes);
buf.rewind();
}
return numReadTotal;
}

@Override
public Long decryptRange(SeekableByteChannel encryptedFile, OutputStream plaintextFile, long pos, long length, boolean authenticate) throws IOException, DecryptFailedException {
encryptedFile.position(pos);

ByteBuffer buf = ByteBuffer.allocate(BUFSIZE);
long numReadTotal = 0;
int numRead = 0;
while ((numRead = encryptedFile.read(buf)) != -1 && numReadTotal < length) {
int len = (int) Math.min(Math.min(numRead, BUFSIZE), length - numReadTotal); // known to fit into integer
numReadTotal += len;
buf.flip();
byte[] bytes = new byte[len];
buf.get(bytes);
plaintextFile.write(bytes);
buf.rewind();
}
return numReadTotal;
}

@Override
public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException {
byte[] buf = new byte[BUFSIZE];
long numWrittenTotal = 0;
int numRead = 0;
while ((numRead = plaintextFile.read(buf)) != -1) {
numWrittenTotal += numRead;
encryptedFile.write(ByteBuffer.wrap(buf, 0, numRead));
}
return numWrittenTotal;
}

}
Expand Up @@ -26,7 +26,7 @@
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.cryptomator.crypto.aes256.Aes256Cryptor;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.webdav.WebDavServer;
import org.cryptomator.webdav.WebDavServer.ServletLifeCycleAdapter;
import org.junit.AfterClass;
Expand All @@ -41,7 +41,7 @@
public class RangeRequestTest {

private static final Logger LOG = LoggerFactory.getLogger(RangeRequestTest.class);
private static final Aes256Cryptor CRYPTOR = new Aes256Cryptor();
private static final Cryptor CRYPTOR = new CryptorMock();
private static final WebDavServer SERVER = new WebDavServer();
private static final File TMP_VAULT = Files.createTempDir();
private static ServletLifeCycleAdapter SERVLET;
Expand Down
11 changes: 11 additions & 0 deletions main/crypto-aes/pom.xml
Expand Up @@ -65,5 +65,16 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<!-- DI -->
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Expand Up @@ -103,14 +103,11 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
/**
* Creates a new Cryptor with a newly initialized PRNG.
*/
public Aes256Cryptor() {
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);
}

Aes256Cryptor(SecureRandom securePrng) {
this.securePrng = securePrng;
// No setSeed needed. See SecureRandom.getInstance(String):
// The first call to nextBytes will force the SecureRandom object to seed itself
}

@Override
Expand Down
@@ -0,0 +1,15 @@
package org.cryptomator.crypto.aes256;

import javax.inject.Singleton;

import org.cryptomator.crypto.Cryptor;

import dagger.Component;

@Singleton
@Component(modules = CryptoModule.class)
interface CryptoComponent {

Cryptor cryptor();

}
@@ -0,0 +1,29 @@
package org.cryptomator.crypto.aes256;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import org.cryptomator.crypto.Cryptor;

import dagger.Module;
import dagger.Provides;

@Module
public class CryptoModule {

@Provides
SecureRandom provideRandomNumberGenerator() {
try {
return SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
// quote "Every implementation of the Java platform is required to support at least one strong SecureRandom implementation."
throw new AssertionError("No SecureRandom implementation available.");
}
}

@Provides
public Cryptor provideCryptor(SecureRandom secureRandom) {
return new Aes256Cryptor(secureRandom);
}

}
Expand Up @@ -19,6 +19,7 @@
import javax.security.auth.DestroyFailedException;

import org.apache.commons.io.IOUtils;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.EncryptFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
Expand All @@ -29,13 +30,18 @@

public class Aes256CryptorTest {

private final Aes256Cryptor cryptor;
private final Cryptor cryptor;

public Aes256CryptorTest() {
cryptor = new Aes256Cryptor();
cryptor = DaggerCryptoTestComponent.create().cryptor();
cryptor.randomizeMasterKey();
}

@Test
public void testMultipleCryptorInstances() {
Assert.assertNotSame(DaggerCryptoTestComponent.create().cryptor(), DaggerCryptoTestComponent.create().cryptor());
}

@Test(timeout = 10000)
public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException, DestroyFailedException, UnsupportedVaultException {
final String pw = "asd";
Expand All @@ -44,7 +50,7 @@ public void testCorrectPassword() throws IOException, WrongPasswordException, De
cryptor.encryptMasterKey(out, pw);
cryptor.destroy();

final Aes256Cryptor decryptor = new Aes256Cryptor();
final Cryptor decryptor = DaggerCryptoTestComponent.create().cryptor();
final InputStream in = new ByteArrayInputStream(out.toByteArray());
decryptor.decryptMasterKey(in, pw);

Expand All @@ -63,7 +69,7 @@ public void testWrongPassword() throws IOException, DecryptFailedException, Wron

// all these passwords are expected to fail.
final String[] wrongPws = {"a", "as", "asdf", "sdf", "das", "dsa", "foo", "bar", "baz"};
final Aes256Cryptor decryptor = new Aes256Cryptor();
final Cryptor decryptor = DaggerCryptoTestComponent.create().cryptor();
for (final String wrongPw : wrongPws) {
final InputStream in = new ByteArrayInputStream(out.toByteArray());
try {
Expand Down
@@ -0,0 +1,15 @@
package org.cryptomator.crypto.aes256;

import javax.inject.Singleton;

import org.cryptomator.crypto.Cryptor;

import dagger.Component;

@Singleton
@Component(modules = CryptoTestModule.class)
public interface CryptoTestComponent {

Cryptor cryptor();

}
@@ -0,0 +1,25 @@
package org.cryptomator.crypto.aes256;

import java.security.SecureRandom;

import org.cryptomator.crypto.Cryptor;

import dagger.Module;
import dagger.Provides;

@Module
public class CryptoTestModule {

@Provides
@SuppressWarnings("deprecation")
SecureRandom provideRandomNumberGenerator() {
// we use this class for testing only, as unit tests on CI servers tend to stall, if they rely on true randomness.
return new InsecureRandomMock();
}

@Provides
Cryptor provideCryptor(SecureRandom secureRandom) {
return new Aes256Cryptor(secureRandom);
}

}
@@ -0,0 +1,23 @@
package org.cryptomator.crypto.aes256;

import java.security.SecureRandom;
import java.util.Random;

/**
* <b>DO NOT USE</b>
*
* This class is for testing only.
*/
@Deprecated // marked as deprecated and made package-private inside /src/test/java to avoid accidential use.
class InsecureRandomMock extends SecureRandom {

private static final long serialVersionUID = 1505563778398085504L;
private final Random random = new Random();

@Override
public void nextBytes(byte[] bytes) {
// let the deterministic RNG do the work:
this.random.nextBytes(bytes);
}

}
4 changes: 2 additions & 2 deletions main/pom.xml
Expand Up @@ -136,12 +136,12 @@
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>2.0.1</version>
<version>2.0.2</version>
<scope>provided</scope>
</dependency>

Expand Down
Expand Up @@ -9,7 +9,7 @@

import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.SamplingCryptorDecorator;
import org.cryptomator.crypto.aes256.Aes256Cryptor;
import org.cryptomator.crypto.aes256.CryptoModule;
import org.cryptomator.ui.model.VaultObjectMapperProvider;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.settings.SettingsProvider;
Expand All @@ -26,7 +26,7 @@
import dagger.Provides;
import javafx.application.Application;

@Module
@Module(includes = CryptoModule.class)
class CryptomatorModule {

private final Application application;
Expand Down Expand Up @@ -90,8 +90,9 @@ WebDavServer provideWebDavServer() {
}

@Provides
Cryptor provideCryptor() {
return SamplingCryptorDecorator.decorate(new Aes256Cryptor());
@Named("SamplingCryptor")
Cryptor provideCryptor(Cryptor cryptor) {
return SamplingCryptorDecorator.decorate(cryptor);
}

private <T> T closeLater(T object, Closer<T> closer) {
Expand Down

0 comments on commit 97a72ec

Please sign in to comment.