Browse Source

- Made unit tests I/O-independent

Sebastian Stenzel 10 năm trước cách đây
mục cha
commit
f76091ddc0

+ 12 - 8
main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java

@@ -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;
 	}

+ 46 - 66
main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java

@@ -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";
+	@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);
+
+		// init cryptor:
 		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 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);
+		// 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();

+ 79 - 0
main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/ByteBufferBackedSeekableChannel.java

@@ -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;
+	}
+
+}