diff --git a/main/filesystem-crypto/pom.xml b/main/filesystem-crypto/pom.xml
index a04425df9..8c21359b3 100644
--- a/main/filesystem-crypto/pom.xml
+++ b/main/filesystem-crypto/pom.xml
@@ -64,6 +64,17 @@
jackson-databind
+
+
+ com.google.dagger
+ dagger
+
+
+ com.google.dagger
+ dagger-compiler
+ provided
+
+
org.cryptomator
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoComponent.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoComponent.java
new file mode 100644
index 000000000..927a0a6ab
--- /dev/null
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoComponent.java
@@ -0,0 +1,15 @@
+package org.cryptomator.crypto;
+
+import javax.inject.Singleton;
+
+import org.cryptomator.crypto.engine.impl.CryptoModule;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = CryptoModule.class)
+interface CryptoComponent {
+
+ CryptoFileSystemFactory cryptoFileSystemFactory();
+
+}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoFileSystemFactory.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoFileSystemFactory.java
new file mode 100644
index 000000000..bca2ce86d
--- /dev/null
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/CryptoFileSystemFactory.java
@@ -0,0 +1,27 @@
+package org.cryptomator.crypto;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.cryptomator.crypto.engine.Cryptor;
+import org.cryptomator.crypto.engine.impl.FileContentCryptorImpl;
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.blockaligned.BlockAlignedFileSystem;
+import org.cryptomator.filesystem.crypto.CryptoFileSystem;
+
+@Singleton
+public class CryptoFileSystemFactory {
+
+ private final Provider cryptorProvider;
+
+ @Inject
+ public CryptoFileSystemFactory(Provider cryptorProvider) {
+ this.cryptorProvider = cryptorProvider;
+ }
+
+ public FileSystem get(Folder root, CharSequence passphrase) {
+ return new BlockAlignedFileSystem(new CryptoFileSystem(root, cryptorProvider.get(), passphrase), FileContentCryptorImpl.CHUNK_SIZE);
+ }
+}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/AuthenticationFailedException.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/AuthenticationFailedException.java
index e2ddaaf4f..76d26eb42 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/AuthenticationFailedException.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/AuthenticationFailedException.java
@@ -10,6 +10,14 @@ package org.cryptomator.crypto.engine;
public class AuthenticationFailedException extends CryptoException {
+ public AuthenticationFailedException() {
+ super();
+ }
+
+ public AuthenticationFailedException(Throwable cause) {
+ super(cause);
+ }
+
public AuthenticationFailedException(String message, Throwable cause) {
super(message, cause);
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/ByteRange.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/ByteRange.java
deleted file mode 100644
index ee7f42cb9..000000000
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/ByteRange.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2015 Sebastian Stenzel and others.
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- * Sebastian Stenzel - initial API and implementation
- *******************************************************************************/
-package org.cryptomator.crypto.engine;
-
-public class ByteRange {
-
- private final long start;
- private final long length;
-
- private ByteRange(long start, long length) {
- if (start < 0) {
- throw new IllegalArgumentException("start must not be a negative value");
- }
- if (length < 0) {
- throw new IllegalArgumentException("length must not be a negative value");
- }
- this.start = start;
- this.length = length;
- }
-
- public static ByteRange of(long start, long length) {
- return new ByteRange(start, length);
- }
-
- /**
- * @return Begin of range (inclusive)
- */
- public long start() {
- return start;
- }
-
- /**
- * @return End of range (exclusive)
- */
- public long end() {
- return start + length;
- }
-
- /**
- * @return Number of bytes between start and end
- */
- public long length() {
- return length;
- }
-}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/CryptoException.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/CryptoException.java
index 5f8c97c05..1c3ad964e 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/CryptoException.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/CryptoException.java
@@ -10,6 +10,14 @@ package org.cryptomator.crypto.engine;
abstract class CryptoException extends RuntimeException {
+ public CryptoException() {
+ super();
+ }
+
+ public CryptoException(Throwable cause) {
+ super(cause);
+ }
+
public CryptoException(String message, Throwable cause) {
super(message, cause);
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentCryptor.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentCryptor.java
index 1850fb3b9..0363c4558 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentCryptor.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentCryptor.java
@@ -23,17 +23,26 @@ public interface FileContentCryptor {
*/
int getHeaderSize();
+ /**
+ * @return The ciphertext position that correlates to the cleartext position.
+ */
+ long toCiphertextPos(long cleartextPos);
+
/**
* @param header The full fixed-length header of an encrypted file. The caller is required to pass the exact amount of bytes returned by {@link #getHeaderSize()}.
+ * @param firstCiphertextByte Position of the first ciphertext byte passed to the decryptor. If the decryptor can not fast-forward to the requested byte, an exception is thrown.
+ * If firstCiphertextByte is an invalid starting point, i.e. doesn't align with the decryptors internal block size, an IllegalArgumentException will be thrown.
* @return A possibly new FileContentDecryptor instance which is capable of decrypting ciphertexts associated with the given file header.
*/
- FileContentDecryptor createFileContentDecryptor(ByteBuffer header);
+ FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte) throws IllegalArgumentException;
/**
* @param header The full fixed-length header of an encrypted file or {@link Optional#empty()}. The caller is required to pass the exact amount of bytes returned by {@link #getHeaderSize()}.
* If the header is empty, a new one will be created by the returned encryptor.
+ * @param firstCleartextByte Position of the first cleartext byte passed to the encryptor. If the encryptor can not fast-forward to the requested byte, an exception is thrown.
+ * If firstCiphertextByte is an invalid starting point, i.e. doesn't align with the encryptors internal block size, an IllegalArgumentException will be thrown.
* @return A possibly new FileContentEncryptor instance which is capable of encrypting cleartext associated with the given file header.
*/
- FileContentEncryptor createFileContentEncryptor(Optional header);
+ FileContentEncryptor createFileContentEncryptor(Optional header, long firstCleartextByte) throws IllegalArgumentException;
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentDecryptor.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentDecryptor.java
index dc5d90c01..cf0b95a3c 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentDecryptor.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentDecryptor.java
@@ -39,24 +39,7 @@ public interface FileContentDecryptor extends Destroyable, Closeable {
*
* @return Decrypted cleartext or {@link FileContentCryptor#EOF}.
*/
- ByteBuffer cleartext() throws InterruptedException;
-
- /**
- * Calculates the ciphertext bytes required to perform a partial decryption of a requested cleartext byte range.
- * If this decryptor doesn't support partial decryption the result will be [0, {@link Long#MAX_VALUE}].
- *
- * @param cleartextRange The cleartext range the caller is interested in.
- * @return The ciphertext range required in order to decrypt the cleartext range.
- */
- ByteRange ciphertextRequiredToDecryptRange(ByteRange cleartextRange);
-
- /**
- * Informs the decryptor, what the first byte of the next ciphertext block will be. This method needs to be called only for partial decryption.
- *
- * @param nextCiphertextByte The first byte of the next ciphertext buffer given via {@link #append(ByteBuffer)}.
- * @throws IllegalArgumentException If nextCiphertextByte is an invalid starting point. Only start bytes determined by {@link #ciphertextRequiredToDecryptRange(ByteRange)} are supported.
- */
- void skipToPosition(long nextCiphertextByte) throws IllegalArgumentException;
+ ByteBuffer cleartext() throws InterruptedException, AuthenticationFailedException;
/**
* Clears file-specific sensitive information.
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentEncryptor.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentEncryptor.java
index 300a64669..3544ede36 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentEncryptor.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FileContentEncryptor.java
@@ -43,23 +43,6 @@ public interface FileContentEncryptor extends Destroyable, Closeable {
*/
ByteBuffer ciphertext() throws InterruptedException;
- /**
- * Calculates the cleartext bytes required to perform a partial encryption of a specific cleartext byte range.
- * If this decryptor doesn't support partial encryption the result will be [0, {@link Long#MAX_VALUE}].
- *
- * @param cleartextRange The cleartext range the caller wants to ecnrypt.
- * @return The cleartext range required in order to encrypt the given cleartext range.
- */
- ByteRange cleartextRequiredToEncryptRange(ByteRange cleartextRange);
-
- /**
- * Informs the encryptor, what the first byte of the next cleartext block will be. This method needs to be called only for partial encryption.
- *
- * @param nextCleartextByte The first byte of the next cleartext buffer given via {@link #append(ByteBuffer)}.
- * @throws IllegalArgumentException If nextCleartextByte is an invalid starting point. Only start bytes determined by {@link #cleartextRequiredToEncryptRange(ByteRange)} are supported.
- */
- void skipToPosition(long nextCleartextByte) throws IllegalArgumentException;
-
/**
* Clears file-specific sensitive information.
*/
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FilenameCryptor.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FilenameCryptor.java
index 595ebe1e4..67a89af67 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FilenameCryptor.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/FilenameCryptor.java
@@ -31,5 +31,5 @@ public interface FilenameCryptor {
* @param ciphertextName Ciphertext only, with any additional strings like file extensions stripped first.
* @return cleartext filename, probably including its cleartext file extension.
*/
- String decryptFilename(String ciphertextName);
+ String decryptFilename(String ciphertextName) throws AuthenticationFailedException;
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/InvalidPassphraseException.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/InvalidPassphraseException.java
new file mode 100644
index 000000000..049c915da
--- /dev/null
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/InvalidPassphraseException.java
@@ -0,0 +1,9 @@
+package org.cryptomator.crypto.engine;
+
+public class InvalidPassphraseException extends CryptoException {
+
+ public InvalidPassphraseException() {
+ super();
+ }
+
+}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptoModule.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptoModule.java
new file mode 100644
index 000000000..4be4e6621
--- /dev/null
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptoModule.java
@@ -0,0 +1,28 @@
+package org.cryptomator.crypto.engine.impl;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import org.cryptomator.crypto.engine.Cryptor;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class CryptoModule {
+
+ @Provides
+ Cryptor provideCryptor(SecureRandom secureRandom) {
+ return new CryptorImpl(secureRandom);
+ }
+
+ @Provides
+ SecureRandom provideSecureRandom() {
+ try {
+ return SecureRandom.getInstanceStrong();
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("No strong PRNGs available.", e);
+ }
+ }
+
+}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java
index 22298a9ec..17b0a721e 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java
@@ -43,27 +43,10 @@ public class CryptorImpl implements Cryptor {
private final AtomicReference fileContentCryptor = new AtomicReference<>();
private final SecureRandom randomSource;
- /**
- * Designated constructor.
- *
- * Package-visible for testing only, use secondary constructors otherwise to ensure a proper PRNG.
- */
CryptorImpl(SecureRandom randomSource) {
this.randomSource = randomSource;
}
- public CryptorImpl() {
- this(getStrongSecureRandom());
- }
-
- private static SecureRandom getStrongSecureRandom() {
- try {
- return SecureRandom.getInstanceStrong();
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("No strong PRNGs available.", e);
- }
- }
-
@Override
public FilenameCryptor getFilenameCryptor() {
assertKeysExist();
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessor.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessor.java
index bec284230..3a502ca30 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessor.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessor.java
@@ -64,15 +64,18 @@ class FifoParallelDataProcessor {
* @return Next job result
* @throws InterruptedException If the calling thread was interrupted while waiting for the next result.
*/
- T processedData() throws InterruptedException {
- try {
- return processedData.take().get();
- } catch (ExecutionException e) {
- if (e.getCause() instanceof RuntimeException) {
- throw (RuntimeException) e.getCause();
- } else {
- throw new RuntimeException(e);
- }
+ T processedData() throws InterruptedException, ExecutionException {
+ return processedData.take().get();
+ }
+
+ /**
+ * Stops work in progress immediatley.
+ */
+ @Deprecated
+ void cancelAllPendingJobs() {
+ Future job;
+ while ((job = processedData.poll()) != null) {
+ job.cancel(true);
}
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java
index 364dbb2e1..144d1555c 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java
@@ -18,13 +18,16 @@ import org.cryptomator.crypto.engine.FileContentCryptor;
import org.cryptomator.crypto.engine.FileContentDecryptor;
import org.cryptomator.crypto.engine.FileContentEncryptor;
-class FileContentCryptorImpl implements FileContentCryptor {
+public class FileContentCryptorImpl implements FileContentCryptor {
+
+ public static final int CHUNK_SIZE = 32 * 1024;
+ static final int MAC_SIZE = 32;
private final SecretKey encryptionKey;
private final SecretKey macKey;
private final SecureRandom randomSource;
- public FileContentCryptorImpl(SecretKey encryptionKey, SecretKey macKey, SecureRandom randomSource) {
+ FileContentCryptorImpl(SecretKey encryptionKey, SecretKey macKey, SecureRandom randomSource) {
this.encryptionKey = encryptionKey;
this.macKey = macKey;
this.randomSource = randomSource;
@@ -36,19 +39,36 @@ class FileContentCryptorImpl implements FileContentCryptor {
}
@Override
- public FileContentDecryptor createFileContentDecryptor(ByteBuffer header) {
- if (header.remaining() != getHeaderSize()) {
- throw new IllegalArgumentException("Invalid header.");
- }
- return new FileContentDecryptorImpl(encryptionKey, macKey, header);
+ public long toCiphertextPos(long cleartextPos) {
+ long chunkNum = cleartextPos / CHUNK_SIZE;
+ long cleartextChunkStart = chunkNum * CHUNK_SIZE;
+ assert cleartextChunkStart <= cleartextPos;
+ long chunkInternalDiff = cleartextPos - cleartextChunkStart;
+ assert chunkInternalDiff >= 0 && chunkInternalDiff < CHUNK_SIZE;
+ long ciphertextChunkStart = chunkNum * (CHUNK_SIZE + MAC_SIZE);
+ return ciphertextChunkStart + chunkInternalDiff;
}
@Override
- public FileContentEncryptor createFileContentEncryptor(Optional header) {
+ public FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte) {
+ if (header.remaining() != getHeaderSize()) {
+ throw new IllegalArgumentException("Invalid header.");
+ }
+ if (firstCiphertextByte % (CHUNK_SIZE + MAC_SIZE) != 0) {
+ throw new IllegalArgumentException("Invalid starting point for decryption.");
+ }
+ return new FileContentDecryptorImpl(encryptionKey, macKey, header, firstCiphertextByte);
+ }
+
+ @Override
+ public FileContentEncryptor createFileContentEncryptor(Optional header, long firstCleartextByte) {
if (header.isPresent() && header.get().remaining() != getHeaderSize()) {
throw new IllegalArgumentException("Invalid header.");
}
- return new FileContentEncryptorImpl(encryptionKey, macKey, randomSource);
+ if (firstCleartextByte % CHUNK_SIZE != 0) {
+ throw new IllegalArgumentException("Invalid starting point for encryption.");
+ }
+ return new FileContentEncryptorImpl(encryptionKey, macKey, randomSource, firstCleartextByte);
}
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java
index 6d426e10d..fd5df0e77 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java
@@ -8,11 +8,15 @@
*******************************************************************************/
package org.cryptomator.crypto.engine.impl;
+import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.CHUNK_SIZE;
+import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.MAC_SIZE;
+
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -20,7 +24,7 @@ import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
-import org.cryptomator.crypto.engine.ByteRange;
+import org.cryptomator.crypto.engine.AuthenticationFailedException;
import org.cryptomator.crypto.engine.FileContentCryptor;
import org.cryptomator.crypto.engine.FileContentDecryptor;
import org.cryptomator.io.ByteBuffers;
@@ -29,8 +33,6 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
private static final int AES_BLOCK_LENGTH_IN_BYTES = 16;
private static final String HMAC_SHA256 = "HmacSHA256";
- private static final int CHUNK_SIZE = 32 * 1024;
- private static final int MAC_SIZE = 32;
private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors();
private static final int READ_AHEAD = 2;
@@ -40,10 +42,11 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
private ByteBuffer ciphertextBuffer = ByteBuffer.allocate(CHUNK_SIZE + MAC_SIZE);
private long chunkNumber = 0;
- public FileContentDecryptorImpl(SecretKey headerKey, SecretKey macKey, ByteBuffer header) {
+ public FileContentDecryptorImpl(SecretKey headerKey, SecretKey macKey, ByteBuffer header, long firstCiphertextByte) {
final ThreadLocalMac hmacSha256 = new ThreadLocalMac(macKey, HMAC_SHA256);
this.hmacSha256 = hmacSha256;
this.header = FileHeader.decrypt(headerKey, hmacSha256, header);
+ this.chunkNumber = firstCiphertextByte / CHUNK_SIZE; // floor() by int-truncation
}
@Override
@@ -73,8 +76,10 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
private void submitCiphertextBuffer() throws InterruptedException {
ciphertextBuffer.flip();
- Callable encryptionJob = new DecryptionJob(ciphertextBuffer, chunkNumber++);
- dataProcessor.submit(encryptionJob);
+ if (ciphertextBuffer.hasRemaining()) {
+ Callable encryptionJob = new DecryptionJob(ciphertextBuffer, chunkNumber++);
+ dataProcessor.submit(encryptionJob);
+ }
}
private void submitEof() throws InterruptedException {
@@ -83,17 +88,15 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
@Override
public ByteBuffer cleartext() throws InterruptedException {
- return dataProcessor.processedData();
- }
-
- @Override
- public ByteRange ciphertextRequiredToDecryptRange(ByteRange cleartextRange) {
- return ByteRange.of(0, Long.MAX_VALUE);
- }
-
- @Override
- public void skipToPosition(long nextCiphertextByte) throws IllegalArgumentException {
- throw new UnsupportedOperationException("Partial decryption not supported.");
+ try {
+ return dataProcessor.processedData();
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof AuthenticationFailedException) {
+ throw new AuthenticationFailedException(e);
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
}
@Override
@@ -130,8 +133,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
Mac mac = hmacSha256.get();
mac.update(ciphertextChunk.asReadOnlyBuffer());
if (!MessageDigest.isEqual(expectedMac, mac.doFinal())) {
- // TODO handle invalid MAC properly
- throw new IllegalArgumentException("Corrupt mac.");
+ throw new AuthenticationFailedException();
}
Cipher cipher = ThreadLocalAesCtrCipher.get();
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java
index 31b9be1e5..12244501e 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java
@@ -8,11 +8,14 @@
*******************************************************************************/
package org.cryptomator.crypto.engine.impl;
+import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.CHUNK_SIZE;
+
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.SecureRandom;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.LongAdder;
import javax.crypto.Cipher;
@@ -21,7 +24,6 @@ import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
-import org.cryptomator.crypto.engine.ByteRange;
import org.cryptomator.crypto.engine.FileContentCryptor;
import org.cryptomator.crypto.engine.FileContentEncryptor;
import org.cryptomator.io.ByteBuffers;
@@ -30,7 +32,6 @@ class FileContentEncryptorImpl implements FileContentEncryptor {
private static final int AES_BLOCK_LENGTH_IN_BYTES = 16;
private static final String HMAC_SHA256 = "HmacSHA256";
- private static final int CHUNK_SIZE = 32 * 1024;
private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors();
private static final int READ_AHEAD = 2;
@@ -42,7 +43,10 @@ class FileContentEncryptorImpl implements FileContentEncryptor {
private ByteBuffer cleartextBuffer = ByteBuffer.allocate(CHUNK_SIZE);
private long chunkNumber = 0;
- public FileContentEncryptorImpl(SecretKey headerKey, SecretKey macKey, SecureRandom randomSource) {
+ public FileContentEncryptorImpl(SecretKey headerKey, SecretKey macKey, SecureRandom randomSource, long firstCleartextByte) {
+ if (firstCleartextByte != 0) {
+ throw new UnsupportedOperationException("Partial encryption not supported.");
+ }
this.hmacSha256 = new ThreadLocalMac(macKey, HMAC_SHA256);
this.headerKey = headerKey;
this.header = new FileHeader(randomSource);
@@ -87,17 +91,11 @@ class FileContentEncryptorImpl implements FileContentEncryptor {
@Override
public ByteBuffer ciphertext() throws InterruptedException {
- return dataProcessor.processedData();
- }
-
- @Override
- public ByteRange cleartextRequiredToEncryptRange(ByteRange cleartextRange) {
- return ByteRange.of(0, Long.MAX_VALUE);
- }
-
- @Override
- public void skipToPosition(long nextCleartextByte) throws IllegalArgumentException {
- throw new UnsupportedOperationException("Partial encryption not supported.");
+ try {
+ return dataProcessor.processedData();
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java
index 693419269..8ed616306 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java
@@ -51,7 +51,7 @@ class FilenameCryptorImpl implements FilenameCryptor {
}
@Override
- public String decryptFilename(String ciphertextName) {
+ public String decryptFilename(String ciphertextName) throws AuthenticationFailedException {
final byte[] encryptedBytes = BASE32.decode(ciphertextName);
try {
final byte[] cleartextBytes = AES_SIV.decrypt(encryptionKey, macKey, encryptedBytes);
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedReadableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedReadableFile.java
index 0f875d3bc..dd4215fbc 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedReadableFile.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedReadableFile.java
@@ -35,11 +35,14 @@ class BlockAlignedReadableFile extends DelegatingReadableFile {
public void position(long logicalPosition) throws UncheckedIOException {
long blockNumber = logicalPosition / blockSize;
long physicalPosition = blockNumber * blockSize;
+ assert physicalPosition <= logicalPosition;
+ int diff = (int) (logicalPosition - physicalPosition);
+ assert diff >= 0;
+ assert diff < blockSize;
super.position(physicalPosition);
eofReached = false;
readCurrentBlock();
- int advance = (int) (logicalPosition - physicalPosition);
- currentBlockBuffer.position(advance);
+ currentBlockBuffer.position(diff);
}
@Override
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java
index efc40bf8c..489334b6f 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java
@@ -44,7 +44,6 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
public int write(ByteBuffer source) throws UncheckedIOException {
int written = 0;
while (source.hasRemaining()) {
- currentBlockBuffer.limit(Math.max(currentBlockBuffer.limit(), Math.min(currentBlockBuffer.position() + source.remaining(), currentBlockBuffer.capacity())));
written += ByteBuffers.copy(source, currentBlockBuffer);
writeCurrentBlockIfNeeded();
}
@@ -53,6 +52,7 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
@Override
public void close() throws UncheckedIOException {
+ currentBlockBuffer.flip();
writeCurrentBlock();
readableFile.close();
super.close();
@@ -73,7 +73,7 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
private void readCurrentBlock() {
currentBlockBuffer.clear();
readableFile.read(currentBlockBuffer);
- currentBlockBuffer.flip();
+ currentBlockBuffer.rewind();
}
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CiphertextReader.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CiphertextReader.java
new file mode 100644
index 000000000..491b954e7
--- /dev/null
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CiphertextReader.java
@@ -0,0 +1,45 @@
+package org.cryptomator.filesystem.crypto;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.Callable;
+
+import org.cryptomator.crypto.engine.FileContentCryptor;
+import org.cryptomator.crypto.engine.FileContentDecryptor;
+import org.cryptomator.filesystem.ReadableFile;
+
+class CiphertextReader implements Callable {
+
+ private static final int READ_BUFFER_SIZE = 32 * 1024 + 32; // aligned with encrypted chunk size + MAC size
+
+ private final ReadableFile file;
+ private final FileContentDecryptor decryptor;
+ private final long startpos;
+
+ public CiphertextReader(ReadableFile file, FileContentDecryptor decryptor, long startpos) {
+ this.file = file;
+ this.decryptor = decryptor;
+ this.startpos = startpos;
+ }
+
+ @Override
+ public Void call() {
+ file.position(startpos);
+ int bytesRead = -1;
+ try {
+ do {
+ ByteBuffer ciphertext = ByteBuffer.allocate(READ_BUFFER_SIZE);
+ file.read(ciphertext);
+ ciphertext.flip();
+ bytesRead = ciphertext.remaining();
+ if (bytesRead > 0) {
+ decryptor.append(ciphertext);
+ }
+ } while (bytesRead > 0);
+ decryptor.append(FileContentCryptor.EOF);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CiphertextWriter.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CiphertextWriter.java
new file mode 100644
index 000000000..7a01d1d8a
--- /dev/null
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CiphertextWriter.java
@@ -0,0 +1,33 @@
+package org.cryptomator.filesystem.crypto;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.Callable;
+
+import org.cryptomator.crypto.engine.FileContentCryptor;
+import org.cryptomator.crypto.engine.FileContentEncryptor;
+import org.cryptomator.filesystem.WritableFile;
+
+class CiphertextWriter implements Callable {
+
+ private final WritableFile file;
+ private final FileContentEncryptor encryptor;
+
+ public CiphertextWriter(WritableFile file, FileContentEncryptor encryptor) {
+ this.file = file;
+ this.encryptor = encryptor;
+ }
+
+ @Override
+ public Void call() {
+ try {
+ ByteBuffer ciphertext;
+ while ((ciphertext = encryptor.ciphertext()) != FileContentCryptor.EOF) {
+ file.write(ciphertext);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystem.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystem.java
index 6482505b6..b19555e20 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystem.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFileSystem.java
@@ -12,6 +12,7 @@ import java.nio.ByteBuffer;
import java.util.Optional;
import org.cryptomator.crypto.engine.Cryptor;
+import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
@@ -27,17 +28,17 @@ public class CryptoFileSystem extends CryptoFolder implements FileSystem {
private final Folder physicalRoot;
- public CryptoFileSystem(Folder physicalRoot, Cryptor cryptor, CharSequence passphrase) {
+ public CryptoFileSystem(Folder physicalRoot, Cryptor cryptor, CharSequence passphrase) throws InvalidPassphraseException {
super(null, "", cryptor);
this.physicalRoot = physicalRoot;
final File masterkeyFile = physicalRoot.file(MASTERKEY_FILENAME);
if (masterkeyFile.exists()) {
final boolean unlocked = decryptMasterKeyFile(cryptor, masterkeyFile, passphrase);
if (!unlocked) {
- // TODO new InvalidPassphraseException() ?
- throw new IllegalArgumentException("Wrong passphrase.");
+ throw new InvalidPassphraseException();
}
} else {
+ cryptor.randomizeMasterkey();
encryptMasterKeyFile(cryptor, masterkeyFile, passphrase);
}
assert masterkeyFile.exists() : "A CryptoFileSystem can not exist without a masterkey file.";
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java
index 0ab451682..a07105b30 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java
@@ -8,18 +8,23 @@
*******************************************************************************/
package org.cryptomator.filesystem.crypto;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
-import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
class CryptoFolder extends CryptoNode implements Folder {
@@ -46,13 +51,10 @@ class CryptoFolder extends CryptoNode implements Folder {
if (directoryId.get() == null) {
File dirFile = physicalFile();
if (dirFile.exists()) {
- try (ReadableFile readable = dirFile.openReadable()) {
- final ByteBuffer buf = ByteBuffer.allocate(64);
- readable.read(buf);
- buf.flip();
- byte[] bytes = new byte[buf.remaining()];
- buf.get(bytes);
- directoryId.set(new String(bytes));
+ try (Reader reader = Channels.newReader(dirFile.openReadable(), StandardCharsets.UTF_8.newDecoder(), -1)) {
+ directoryId.set(IOUtils.toString(reader));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
} else {
directoryId.compareAndSet(null, UUID.randomUUID().toString());
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoReadableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoReadableFile.java
index 570c30da5..82d72be56 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoReadableFile.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoReadableFile.java
@@ -10,7 +10,6 @@ package org.cryptomator.filesystem.crypto;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@@ -24,35 +23,24 @@ import org.cryptomator.io.ByteBuffers;
class CryptoReadableFile implements ReadableFile {
- private static final int READ_BUFFER_SIZE = 32 * 1024 + 32; // aligned with
- // encrypted
- // chunk size +
- // MAC size
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
private final ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
- private final FileContentDecryptor decryptor;
+ private final ByteBuffer header;
+ private final FileContentCryptor cryptor;
private final ReadableFile file;
+ private FileContentDecryptor decryptor;
private Future readAheadTask;
private ByteBuffer bufferedCleartext = EMPTY_BUFFER;
public CryptoReadableFile(FileContentCryptor cryptor, ReadableFile file) {
- final int headerSize = cryptor.getHeaderSize();
- final ByteBuffer header = ByteBuffer.allocate(headerSize);
+ this.header = ByteBuffer.allocate(cryptor.getHeaderSize());
+ this.cryptor = cryptor;
+ this.file = file;
file.position(0);
file.read(header);
header.flip();
- this.decryptor = cryptor.createFileContentDecryptor(header);
- this.file = file;
- this.prepareReadAtPhysicalPosition(headerSize + 0);
- }
-
- private void prepareReadAtPhysicalPosition(long pos) {
- if (readAheadTask != null) {
- readAheadTask.cancel(true);
- bufferedCleartext = EMPTY_BUFFER;
- }
- readAheadTask = executorService.submit(new Reader(pos));
+ this.position(0);
}
@Override
@@ -75,7 +63,13 @@ class CryptoReadableFile implements ReadableFile {
@Override
public void position(long position) throws UncheckedIOException {
- throw new UnsupportedOperationException("Partial read unsupported");
+ if (readAheadTask != null) {
+ readAheadTask.cancel(true);
+ bufferedCleartext = EMPTY_BUFFER;
+ }
+ long ciphertextPos = cryptor.toCiphertextPos(position);
+ decryptor = cryptor.createFileContentDecryptor(header.asReadOnlyBuffer(), ciphertextPos);
+ readAheadTask = executorService.submit(new CiphertextReader(file, decryptor, header.remaining() + ciphertextPos));
}
private void bufferCleartext() throws InterruptedException {
@@ -110,35 +104,4 @@ class CryptoReadableFile implements ReadableFile {
file.close();
}
- private class Reader implements Callable {
-
- private final long startpos;
-
- public Reader(long startpos) {
- this.startpos = startpos;
- }
-
- @Override
- public Void call() {
- file.position(startpos);
- int bytesRead = -1;
- try {
- do {
- ByteBuffer ciphertext = ByteBuffer.allocate(READ_BUFFER_SIZE);
- file.read(ciphertext);
- ciphertext.flip();
- bytesRead = ciphertext.remaining();
- if (bytesRead > 0) {
- decryptor.append(ciphertext);
- }
- } while (bytesRead > 0);
- decryptor.append(FileContentCryptor.EOF);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- return null;
- }
-
- }
-
}
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java
index d9e34625a..bd2bc5faa 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoWritableFile.java
@@ -12,7 +12,6 @@ import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Optional;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -33,9 +32,9 @@ class CryptoWritableFile implements WritableFile {
public CryptoWritableFile(FileContentCryptor cryptor, WritableFile file) {
this.file = file;
- this.encryptor = cryptor.createFileContentEncryptor(Optional.empty());
+ this.encryptor = cryptor.createFileContentEncryptor(Optional.empty(), 0);
writeHeader();
- this.writeTask = executorService.submit(new Writer());
+ this.writeTask = executorService.submit(new CiphertextWriter(file, encryptor));
}
private void writeHeader() {
@@ -119,21 +118,4 @@ class CryptoWritableFile implements WritableFile {
}
}
- private class Writer implements Callable {
-
- @Override
- public Void call() {
- try {
- ByteBuffer ciphertext;
- while ((ciphertext = encryptor.ciphertext()) != FileContentCryptor.EOF) {
- file.write(ciphertext);
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- return null;
- }
-
- }
-
}
diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoFileSystemIntegrationTest.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoFileSystemIntegrationTest.java
new file mode 100644
index 000000000..e7ff9c454
--- /dev/null
+++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoFileSystemIntegrationTest.java
@@ -0,0 +1,60 @@
+package org.cryptomator.crypto;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import org.cryptomator.filesystem.File;
+import org.cryptomator.filesystem.FileSystem;
+import org.cryptomator.filesystem.Folder;
+import org.cryptomator.filesystem.ReadableFile;
+import org.cryptomator.filesystem.WritableFile;
+import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CryptoFileSystemIntegrationTest {
+
+ private FileSystem ciphertextFs;
+ private FileSystem cleartextFs;
+
+ @Before
+ public void setupFileSystems() {
+ ciphertextFs = new InMemoryFileSystem();
+ // final Predicate isMetadataFolder = (Node node) -> node.equals(physicalFs.folder("m"));
+ // final FileSystem metadataHidingFs = new BlacklistingFileSystem(physicalFs, isMetadataFolder);
+ // final FileSystem shorteningFs = new ShorteningFileSystem(metadataHidingFs, physicalFs.folder("m"), 70);
+ cleartextFs = DaggerCryptoTestComponent.create().cryptoFileSystemFactory().get(ciphertextFs, "TopSecret");
+ cleartextFs.create();
+ }
+
+ @Test
+ public void testRandomAccess() {
+ File cleartextFile = cleartextFs.file("test");
+ try (WritableFile writable = cleartextFile.openWritable()) {
+ ByteBuffer buf = ByteBuffer.allocate(25000);
+ for (int i = 0; i < 40; i++) { // 40 * 25k = 1M
+ buf.clear();
+ Arrays.fill(buf.array(), (byte) i);
+ writable.write(buf);
+ }
+ }
+
+ Folder ciphertextRootFolder = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get();
+ Assert.assertTrue(ciphertextRootFolder.exists());
+ File ciphertextFile = ciphertextRootFolder.files().findAny().get();
+ Assert.assertTrue(ciphertextFile.exists());
+
+ try (ReadableFile readable = cleartextFile.openReadable()) {
+ ByteBuffer buf = ByteBuffer.allocate(1);
+ for (int i = 0; i < 40; i++) {
+ buf.clear();
+ readable.position(i * 25000 + (long) Math.random() * 24999);
+ readable.read(buf);
+ buf.flip();
+ Assert.assertEquals(i, buf.get());
+ }
+ }
+ }
+
+}
diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoTestComponent.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoTestComponent.java
new file mode 100644
index 000000000..d98995407
--- /dev/null
+++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/CryptoTestComponent.java
@@ -0,0 +1,15 @@
+package org.cryptomator.crypto;
+
+import javax.inject.Singleton;
+
+import org.cryptomator.crypto.engine.impl.CryptoTestModule;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = CryptoTestModule.class)
+interface CryptoTestComponent {
+
+ CryptoFileSystemFactory cryptoFileSystemFactory();
+
+}
diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFileContentCryptor.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFileContentCryptor.java
index b40d03ffb..6eebffc2f 100644
--- a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFileContentCryptor.java
+++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFileContentCryptor.java
@@ -21,7 +21,12 @@ class NoFileContentCryptor implements FileContentCryptor {
}
@Override
- public FileContentDecryptor createFileContentDecryptor(ByteBuffer header) {
+ public long toCiphertextPos(long cleartextPos) {
+ return cleartextPos;
+ }
+
+ @Override
+ public FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte) {
if (header.remaining() != getHeaderSize()) {
throw new IllegalArgumentException("Invalid header size.");
}
@@ -29,7 +34,7 @@ class NoFileContentCryptor implements FileContentCryptor {
}
@Override
- public FileContentEncryptor createFileContentEncryptor(Optional header) {
+ public FileContentEncryptor createFileContentEncryptor(Optional header, long firstCleartextByte) {
return new Encryptor();
}
@@ -66,16 +71,6 @@ class NoFileContentCryptor implements FileContentCryptor {
return cleartextQueue.take();
}
- @Override
- public ByteRange ciphertextRequiredToDecryptRange(ByteRange cleartextRange) {
- return cleartextRange;
- }
-
- @Override
- public void skipToPosition(long nextCiphertextByte) throws IllegalArgumentException {
- // no-op
- }
-
@Override
public void destroy() {
// no-op
@@ -115,16 +110,6 @@ class NoFileContentCryptor implements FileContentCryptor {
return ciphertextQueue.take();
}
- @Override
- public ByteRange cleartextRequiredToEncryptRange(ByteRange cleartextRange) {
- return cleartextRange;
- }
-
- @Override
- public void skipToPosition(long nextCleartextByte) throws IllegalArgumentException {
- // no-op
- }
-
@Override
public void destroy() {
// no-op
diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptoTestModule.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptoTestModule.java
new file mode 100644
index 000000000..5f9c31165
--- /dev/null
+++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/CryptoTestModule.java
@@ -0,0 +1,31 @@
+package org.cryptomator.crypto.engine.impl;
+
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import org.cryptomator.crypto.engine.Cryptor;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class CryptoTestModule {
+
+ @Provides
+ Cryptor provideCryptor(SecureRandom secureRandom) {
+ return new CryptorImpl(secureRandom);
+ }
+
+ @Provides
+ SecureRandom provideSecureRandom() {
+ return new SecureRandom() {
+
+ @Override
+ public void nextBytes(byte[] bytes) {
+ Arrays.fill(bytes, (byte) 0x00);
+ }
+
+ };
+ }
+
+}
diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessorTest.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessorTest.java
index 0314ef7ae..56d0a4c87 100644
--- a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessorTest.java
+++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FifoParallelDataProcessorTest.java
@@ -10,6 +10,7 @@ package org.cryptomator.crypto.engine.impl;
import java.lang.reflect.Field;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -19,25 +20,12 @@ import org.junit.Test;
public class FifoParallelDataProcessorTest {
- @Test(expected = IllegalStateException.class)
- public void testRethrowsException() throws InterruptedException {
+ @Test(expected = ExecutionException.class)
+ public void testRethrowsExceptionAsExecutionException() throws InterruptedException, ExecutionException {
FifoParallelDataProcessor