- Made unit tests I/O-independent

This commit is contained in:
Sebastian Stenzel
2014-12-20 16:46:50 +01:00
parent 6dff296872
commit f76091ddc0
3 changed files with 138 additions and 75 deletions

View File

@@ -89,7 +89,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
*/
private final byte[] masterKey = new byte[MASTER_KEY_LENGTH];
private static final int SIZE_OF_LONG = Long.SIZE / Byte.SIZE;
private static final int SIZE_OF_LONG = Long.BYTES;
static {
try {
@@ -444,15 +444,19 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
// use an IV, whose last 8 bytes store a long used in counter mode and write initial value to file.
final ByteBuffer countingIv = ByteBuffer.wrap(randomData(AES_BLOCK_LENGTH));
countingIv.putLong(AES_BLOCK_LENGTH - SIZE_OF_LONG, 0l);
countingIv.position(0);
// derive secret key and generate cipher:
final SecretKey key = this.pbkdf2(masterKey, EMPTY_SALT, PBKDF2_MASTERKEY_ITERATIONS, AES_KEY_LENGTH);
final Cipher cipher = this.cipher(FILE_CONTENT_CIPHER, key, countingIv.array(), Cipher.ENCRYPT_MODE);
// skip 8 bytes (reserved for file size):
encryptedFile.position(SIZE_OF_LONG);
// 8 bytes (file size: temporarily -1):
final ByteBuffer fileSize = ByteBuffer.allocate(SIZE_OF_LONG);
fileSize.putLong(-1L);
fileSize.position(0);
encryptedFile.write(fileSize);
// write iv:
// 16 bytes (iv):
encryptedFile.write(countingIv);
// write content:
@@ -461,11 +465,11 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
final Long actualSize = IOUtils.copyLarge(plaintextFile, cipheredOut);
// write filesize
final ByteBuffer actualSizeBuffer = ByteBuffer.allocate(SIZE_OF_LONG);
actualSizeBuffer.putLong(actualSize);
actualSizeBuffer.position(0);
fileSize.position(0);
fileSize.putLong(actualSize);
fileSize.position(0);
encryptedFile.position(0);
encryptedFile.write(actualSizeBuffer);
encryptedFile.write(fileSize);
return actualSize;
}

View File

@@ -12,96 +12,84 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.cryptomator.crypto.CryptorIOSupport;
import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
import org.cryptomator.crypto.exceptions.WrongPasswordException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class Aes256CryptorTest {
private static final Random TEST_PRNG = new Random();
private Path tmpDir;
private Path masterKey;
private Path encryptedFile;
@Before
public void prepareTmpDir() throws IOException {
final String tmpDirName = (String) System.getProperties().get("java.io.tmpdir");
final Path path = FileSystems.getDefault().getPath(tmpDirName);
tmpDir = Files.createTempDirectory(path, "oce-crypto-test");
masterKey = tmpDir.resolve("test" + Aes256Cryptor.MASTERKEY_FILE_EXT);
encryptedFile = tmpDir.resolve("test" + Aes256Cryptor.BASIC_FILE_EXT);
}
@After
public void dropTmpDir() {
try {
FileUtils.deleteDirectory(tmpDir.toFile());
} catch (IOException e) {
// ignore
}
}
/* ------------------------------------------------------------------------------- */
@Test
public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
final Aes256Cryptor decryptor = new Aes256Cryptor(TEST_PRNG);
final InputStream in = Files.newInputStream(masterKey, StandardOpenOption.READ);
final InputStream in = new ByteArrayInputStream(out.toByteArray());
decryptor.decryptMasterKey(in, pw);
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
@Test(expected = WrongPasswordException.class)
public void testWrongPassword() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
final String wrongPw = "foo";
final Aes256Cryptor decryptor = new Aes256Cryptor(TEST_PRNG);
final InputStream in = Files.newInputStream(masterKey, StandardOpenOption.READ);
final InputStream in = new ByteArrayInputStream(out.toByteArray());
decryptor.decryptMasterKey(in, wrongPw);
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
@Test(expected = NoSuchFileException.class)
public void testWrongLocation() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@Test
public void testEncryptionAndDecryption() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException {
// our test plaintext data:
final byte[] plaintextData = "Hello World".getBytes();
final InputStream plaintextIn = new ByteArrayInputStream(plaintextData);
final Path wrongMasterKey = tmpDir.resolve("notExistingMasterKey.json");
final Aes256Cryptor decryptor = new Aes256Cryptor(TEST_PRNG);
final InputStream in = Files.newInputStream(wrongMasterKey, StandardOpenOption.READ);
decryptor.decryptMasterKey(in, pw);
// init cryptor:
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
// encrypt:
final ByteBuffer encryptedData = ByteBuffer.allocate(plaintextData.length + 200);
final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData);
cryptor.encryptFile(plaintextIn, encryptedOut);
IOUtils.closeQuietly(plaintextIn);
IOUtils.closeQuietly(encryptedOut);
// decrypt:
final SeekableByteChannel encryptedIn = new ByteBufferBackedSeekableChannel(encryptedData);
final ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream();
final Long numDecryptedBytes = cryptor.decryptedFile(encryptedIn, plaintextOut);
IOUtils.closeQuietly(encryptedIn);
IOUtils.closeQuietly(plaintextOut);
Assert.assertTrue(numDecryptedBytes > 0);
// check decrypted data:
final byte[] result = plaintextOut.toByteArray();
Assert.assertArrayEquals(plaintextData, result);
}
@Test
@@ -118,16 +106,21 @@ public class Aes256CryptorTest {
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
// encrypt:
final SeekableByteChannel fileOut = Files.newByteChannel(encryptedFile, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.encryptFile(plaintextIn, fileOut);
fileOut.close();
final ByteBuffer encryptedData = ByteBuffer.allocate(plaintextData.length + 200);
final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData);
cryptor.encryptFile(plaintextIn, encryptedOut);
IOUtils.closeQuietly(plaintextIn);
IOUtils.closeQuietly(encryptedOut);
// decrypt:
final SeekableByteChannel fileIn = Files.newByteChannel(encryptedFile, StandardOpenOption.READ);
final SeekableByteChannel encryptedIn = new ByteBufferBackedSeekableChannel(encryptedData);
final ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream();
final Long numDecryptedBytes = cryptor.decryptRange(fileIn, plaintextOut, 313 * Integer.BYTES, 50 * Integer.BYTES);
final Long numDecryptedBytes = cryptor.decryptRange(encryptedIn, plaintextOut, 313 * Integer.BYTES, 50 * Integer.BYTES);
IOUtils.closeQuietly(encryptedIn);
IOUtils.closeQuietly(plaintextOut);
Assert.assertTrue(numDecryptedBytes > 0);
// check decrypted data:
final byte[] result = plaintextOut.toByteArray();
final byte[] expected = new byte[50 * Integer.BYTES];
final ByteBuffer bbOut = ByteBuffer.wrap(expected);
@@ -137,19 +130,6 @@ public class Aes256CryptorTest {
Assert.assertArrayEquals(expected, result);
}
@Test(expected = FileAlreadyExistsException.class)
public void testReInitialization() throws IOException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor(TEST_PRNG);
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
final OutputStream outAgain = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
cryptor.encryptMasterKey(outAgain, pw);
cryptor.swipeSensitiveData();
}
@Test
public void testEncryptionOfFilenames() throws IOException {
final CryptorIOSupport ioSupportMock = new CryptoIOSupportMock();

View File

@@ -0,0 +1,79 @@
package org.cryptomator.crypto.aes256;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
class ByteBufferBackedSeekableChannel implements SeekableByteChannel {
private final ByteBuffer buffer;
private boolean open = true;
ByteBufferBackedSeekableChannel(ByteBuffer buffer) {
this.buffer = buffer;
}
@Override
public boolean isOpen() {
return open;
}
@Override
public void close() throws IOException {
open = false;
}
@Override
public int read(ByteBuffer dst) throws IOException {
if (buffer.remaining() == 0) {
return -1;
}
int num = Math.min(dst.remaining(), buffer.remaining());
byte[] bytes = new byte[num];
buffer.get(bytes);
dst.put(bytes);
return num;
}
@Override
public int write(ByteBuffer src) throws IOException {
int num = src.remaining();
if (buffer.remaining() < src.remaining()) {
buffer.limit(buffer.limit() + src.remaining());
}
buffer.put(src);
return num;
}
@Override
public long position() throws IOException {
return buffer.position();
}
@Override
public SeekableByteChannel position(long newPosition) throws IOException {
if (newPosition > Integer.MAX_VALUE) {
throw new UnsupportedOperationException();
}
if (newPosition > buffer.limit()) {
buffer.limit((int) newPosition);
}
buffer.position((int) newPosition);
return this;
}
@Override
public long size() throws IOException {
return buffer.limit();
}
@Override
public SeekableByteChannel truncate(long size) throws IOException {
if (size > Integer.MAX_VALUE) {
throw new UnsupportedOperationException();
}
buffer.limit((int) size);
return this;
}
}