support for forced decryption ignoring failed authentication

This commit is contained in:
Sebastian Stenzel
2016-01-23 16:49:04 +01:00
parent a6bbc0ed44
commit 2e5264bac2
16 changed files with 166 additions and 36 deletions

View File

@@ -32,9 +32,10 @@ public interface FileContentCryptor {
* @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.
* @param authenticate Skip authentication by setting this flag to <code>false</code>. Should be <code>true</code> by default.
* @return A possibly new FileContentDecryptor instance which is capable of decrypting ciphertexts associated with the given file header.
*/
FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte) throws IllegalArgumentException;
FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte, boolean authenticate) 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()}.

View File

@@ -50,14 +50,14 @@ public class FileContentCryptorImpl implements FileContentCryptor {
}
@Override
public FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte) {
public FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte, boolean authenticate) {
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);
return new FileContentDecryptorImpl(encryptionKey, macKey, header, firstCiphertextByte, authenticate);
}
@Override

View File

@@ -41,13 +41,15 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
private final FifoParallelDataProcessor<ByteBuffer> dataProcessor = new FifoParallelDataProcessor<>(NUM_THREADS, NUM_THREADS + READ_AHEAD);
private final ThreadLocal<Mac> hmacSha256;
private final FileHeader header;
private final boolean authenticate;
private ByteBuffer ciphertextBuffer = ByteBuffer.allocate(CHUNK_SIZE + MAC_SIZE);
private long chunkNumber = 0;
public FileContentDecryptorImpl(SecretKey headerKey, SecretKey macKey, ByteBuffer header, long firstCiphertextByte) {
public FileContentDecryptorImpl(SecretKey headerKey, SecretKey macKey, ByteBuffer header, long firstCiphertextByte, boolean authenticate) {
final ThreadLocalMac hmacSha256 = new ThreadLocalMac(macKey, HMAC_SHA256);
this.hmacSha256 = hmacSha256;
this.header = FileHeader.decrypt(headerKey, hmacSha256, header);
this.authenticate = authenticate;
this.chunkNumber = firstCiphertextByte / CHUNK_SIZE; // floor() by int-truncation
}
@@ -141,10 +143,12 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
@Override
public ByteBuffer call() {
try {
Mac mac = hmacSha256.get();
mac.update(ciphertextChunk.asReadOnlyBuffer());
if (!MessageDigest.isEqual(expectedMac, mac.doFinal())) {
throw new AuthenticationFailedException();
if (authenticate) {
Mac mac = hmacSha256.get();
mac.update(ciphertextChunk.asReadOnlyBuffer());
if (!MessageDigest.isEqual(expectedMac, mac.doFinal())) {
throw new AuthenticationFailedException();
}
}
Cipher cipher = ThreadLocalAesCtrCipher.get();

View File

@@ -40,7 +40,8 @@ public class CryptoFile extends CryptoNode implements File {
@Override
public ReadableFile openReadable() {
return new CryptoReadableFile(cryptor.getFileContentCryptor(), physicalFile().openReadable());
boolean authenticate = !fileSystem().delegate().shouldSkipAuthentication(toString());
return new CryptoReadableFile(cryptor.getFileContentCryptor(), physicalFile().openReadable(), authenticate);
}
@Override

View File

@@ -27,10 +27,12 @@ public class CryptoFileSystem extends CryptoFolder implements FileSystem {
private static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup";
private final Folder physicalRoot;
private final CryptoFileSystemDelegate delegate;
public CryptoFileSystem(Folder physicalRoot, Cryptor cryptor, CharSequence passphrase) throws InvalidPassphraseException {
public CryptoFileSystem(Folder physicalRoot, Cryptor cryptor, CryptoFileSystemDelegate delegate, CharSequence passphrase) throws InvalidPassphraseException {
super(null, "", cryptor);
this.physicalRoot = physicalRoot;
this.delegate = delegate;
final File masterkeyFile = physicalRoot.file(MASTERKEY_FILENAME);
if (masterkeyFile.exists()) {
final boolean unlocked = decryptMasterKeyFile(cryptor, masterkeyFile, passphrase);
@@ -68,6 +70,10 @@ public class CryptoFileSystem extends CryptoFolder implements FileSystem {
}
}
CryptoFileSystemDelegate delegate() {
return delegate;
}
@Override
protected File physicalFile() {
return physicalDataRoot().file(ROOT_DIR_FILE);
@@ -107,7 +113,7 @@ public class CryptoFileSystem extends CryptoFolder implements FileSystem {
@Override
public String toString() {
return physicalRoot + ":::/";
return "/";
}
}

View File

@@ -0,0 +1,21 @@
package org.cryptomator.filesystem.crypto;
public interface CryptoFileSystemDelegate {
/**
* Reports the path for resources, that could not be decrypted due to authentication errors.
*
* @param cleartextPath Unix-style vault-relative path
*/
void authenticationFailed(String cleartextPath);
/**
* Allows the delegate to deactivate authentication during decryption.
* This bears the risk of CCAs, thus this method should only return <code>true</code> for data recovery purposes.
*
* @param cleartextPath Unix-style vault-relative path
* @return Must always <b>default to <code>false</code></b>, except when authentication should be skipped.
*/
boolean shouldSkipAuthentication(String cleartextPath);
}

View File

@@ -24,9 +24,9 @@ public class CryptoFileSystemFactory {
this.blockAlignedFileSystemFactory = blockAlignedFileSystemFactory;
}
public FileSystem get(Folder root, CharSequence passphrase) {
public FileSystem get(Folder root, CharSequence passphrase, CryptoFileSystemDelegate delegate) {
final FileSystem nameShorteningFs = shorteningFileSystemFactory.get(root);
final FileSystem cryptoFs = new CryptoFileSystem(nameShorteningFs, cryptorProvider.get(), passphrase);
final FileSystem cryptoFs = new CryptoFileSystem(nameShorteningFs, cryptorProvider.get(), delegate, passphrase);
return blockAlignedFileSystemFactory.get(cryptoFs);
}
}

View File

@@ -37,6 +37,11 @@ abstract class CryptoNode implements Node {
return parent.physicalFolder().file(encryptedName());
}
@Override
public CryptoFileSystem fileSystem() {
return (CryptoFileSystem) Node.super.fileSystem();
}
@Override
public Optional<CryptoFolder> parent() {
return Optional.of(parent);
@@ -74,4 +79,12 @@ abstract class CryptoNode implements Node {
}
}
/**
* Unix-style cleartext path rooted at the vault's top-level directory.
*
* @return Vault-relative cleartext path.
*/
@Override
public abstract String toString();
}

View File

@@ -29,14 +29,16 @@ class CryptoReadableFile implements ReadableFile {
private final ByteBuffer header;
private final FileContentCryptor cryptor;
private final ReadableFile file;
private final boolean authenticate;
private FileContentDecryptor decryptor;
private Future<Void> readAheadTask;
private ByteBuffer bufferedCleartext = EMPTY_BUFFER;
public CryptoReadableFile(FileContentCryptor cryptor, ReadableFile file) {
public CryptoReadableFile(FileContentCryptor cryptor, ReadableFile file, boolean authenticate) {
this.header = ByteBuffer.allocate(cryptor.getHeaderSize());
this.cryptor = cryptor;
this.file = file;
this.authenticate = authenticate;
file.position(0);
file.read(header);
header.flip();
@@ -73,7 +75,7 @@ class CryptoReadableFile implements ReadableFile {
bufferedCleartext = EMPTY_BUFFER;
}
long ciphertextPos = cryptor.toCiphertextPos(position);
decryptor = cryptor.createFileContentDecryptor(header.asReadOnlyBuffer(), ciphertextPos);
decryptor = cryptor.createFileContentDecryptor(header.asReadOnlyBuffer(), ciphertextPos, authenticate);
readAheadTask = executorService.submit(new CiphertextReader(file, decryptor, header.remaining() + ciphertextPos));
}

View File

@@ -29,7 +29,7 @@ class NoFileContentCryptor implements FileContentCryptor {
}
@Override
public FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte) {
public FileContentDecryptor createFileContentDecryptor(ByteBuffer header, long firstCiphertextByte, boolean authenticate) {
if (header.remaining() != getHeaderSize()) {
throw new IllegalArgumentException("Invalid header size.");
}

View File

@@ -53,7 +53,7 @@ public class FileContentCryptorImplTest {
FileContentCryptor cryptor = new FileContentCryptorImpl(encryptionKey, macKey, RANDOM_MOCK);
ByteBuffer tooShortHeader = ByteBuffer.allocate(63);
cryptor.createFileContentDecryptor(tooShortHeader, 0);
cryptor.createFileContentDecryptor(tooShortHeader, 0, true);
}
@Test(expected = IllegalArgumentException.class)
@@ -75,7 +75,7 @@ public class FileContentCryptorImplTest {
FileContentCryptor cryptor = new FileContentCryptorImpl(encryptionKey, macKey, RANDOM_MOCK);
ByteBuffer header = ByteBuffer.allocate(cryptor.getHeaderSize());
cryptor.createFileContentDecryptor(header, 3);
cryptor.createFileContentDecryptor(header, 3, true);
}
@Test(expected = IllegalArgumentException.class)
@@ -110,7 +110,7 @@ public class FileContentCryptorImplTest {
ciphertext.flip();
ByteBuffer plaintext = ByteBuffer.allocate(100);
try (FileContentDecryptor decryptor = cryptor.createFileContentDecryptor(header, 0)) {
try (FileContentDecryptor decryptor = cryptor.createFileContentDecryptor(header, 0, true)) {
decryptor.append(ciphertext);
decryptor.append(FileContentCryptor.EOF);
ByteBuffer buf;
@@ -163,7 +163,7 @@ public class FileContentCryptorImplTest {
final Thread fileReader;
final long decStart = System.nanoTime();
try (FileContentDecryptor decryptor = cryptor.createFileContentDecryptor(header, 0)) {
try (FileContentDecryptor decryptor = cryptor.createFileContentDecryptor(header, 0, true)) {
fileReader = new Thread(() -> {
try (FileChannel fc = FileChannel.open(tmpFile, StandardOpenOption.READ)) {
ByteBuffer ciphertext = ByteBuffer.allocate(654321);

View File

@@ -19,6 +19,7 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Base64;
import org.cryptomator.crypto.engine.AuthenticationFailedException;
import org.cryptomator.crypto.engine.FileContentCryptor;
import org.cryptomator.crypto.engine.FileContentDecryptor;
import org.cryptomator.crypto.engine.FileContentEncryptor;
@@ -47,7 +48,51 @@ public class FileContentDecryptorImplTest {
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHQ==");
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0)) {
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) {
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15)));
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43)));
decryptor.append(FileContentCryptor.EOF);
ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext.
ByteBuffer buf;
while ((buf = decryptor.cleartext()) != FileContentCryptor.EOF) {
ByteBuffers.copy(buf, result);
}
Assert.assertArrayEquals("hello world".getBytes(), result.array());
}
}
@Test(expected = AuthenticationFailedException.class)
public void testManipulatedDecryption() throws InterruptedException {
final byte[] keyBytes = new byte[32];
final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES");
final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256");
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHq==");
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) {
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15)));
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43)));
decryptor.append(FileContentCryptor.EOF);
ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext.
ByteBuffer buf;
while ((buf = decryptor.cleartext()) != FileContentCryptor.EOF) {
ByteBuffers.copy(buf, result);
}
}
}
@Test
public void testManipulatedDecryptionWithSuppressedAuthentication() throws InterruptedException {
final byte[] keyBytes = new byte[32];
final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES");
final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256");
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHq==");
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, false)) {
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15)));
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43)));
decryptor.append(FileContentCryptor.EOF);
@@ -69,7 +114,7 @@ public class FileContentDecryptorImplTest {
final SecretKey macKey = new SecretKeySpec(keyBytes, "AES");
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0)) {
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) {
decryptor.cancelWithException(new IOException("can not do"));
decryptor.cleartext();
}
@@ -113,7 +158,7 @@ public class FileContentDecryptorImplTest {
for (int i = 3; i >= 0; i--) {
final int ciphertextPos = (int) cryptor.toCiphertextPos(i * 32768);
try (FileContentDecryptor decryptor = cryptor.createFileContentDecryptor(header, ciphertextPos)) {
try (FileContentDecryptor decryptor = cryptor.createFileContentDecryptor(header, ciphertextPos, true)) {
final Thread ciphertextReader = new Thread(() -> {
try {
ciphertext.position(ciphertextPos);

View File

@@ -15,6 +15,7 @@ import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,13 +25,15 @@ public class CryptoFileSystemComponentIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(CryptoFileSystemComponentIntegrationTest.class);
private CryptoFileSystemDelegate cryptoDelegate;
private FileSystem ciphertextFs;
private FileSystem cleartextFs;
@Before
public void setupFileSystems() {
cryptoDelegate = Mockito.mock(CryptoFileSystemDelegate.class);
ciphertextFs = new InMemoryFileSystem();
cleartextFs = cryptoFsComp.cryptoFileSystemFactory().get(ciphertextFs, "TopSecret");
cleartextFs = cryptoFsComp.cryptoFileSystemFactory().get(ciphertextFs, "TopSecret", cryptoDelegate);
cleartextFs.create();
}
@@ -77,7 +80,38 @@ public class CryptoFileSystemComponentIntegrationTest {
}
}
@Test(timeout = 2000000) // assuming a minimum speed of 10mb/s during encryption and decryption 20s should be enough
@Test
public void testForcedDecryptionOfManipulatedFile() {
// write test content to encrypted file
try (WritableFile writable = cleartextFs.file("test1.txt").openWritable()) {
writable.write(ByteBuffer.wrap("Hello World".getBytes()));
}
File physicalFile = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get().files().findAny().get();
Assert.assertTrue(physicalFile.exists());
// toggle last bit
try (WritableFile writable = physicalFile.openWritable(); ReadableFile readable = physicalFile.openReadable()) {
ByteBuffer buf = ByteBuffer.allocate((int) readable.size());
readable.read(buf);
buf.array()[buf.limit() - 1] ^= 0x01;
buf.flip();
writable.write(buf);
}
// whitelist
Mockito.when(cryptoDelegate.shouldSkipAuthentication("/test1.txt")).thenReturn(true);
// read test content from decrypted file
try (ReadableFile readable = cleartextFs.file("test1.txt").openReadable()) {
ByteBuffer buf = ByteBuffer.allocate(11);
readable.read(buf);
buf.flip();
Assert.assertArrayEquals("Hello World".getBytes(), buf.array());
}
}
@Test(timeout = 20000) // assuming a minimum speed of 10mb/s during encryption and decryption 20s should be enough
public void testEncryptionAndDecryptionSpeed() throws InterruptedException, IOException {
File file = cleartextFs.file("benchmark.test");

View File

@@ -27,6 +27,7 @@ import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class CryptoFileSystemTest {
@@ -45,7 +46,7 @@ public class CryptoFileSystemTest {
Assert.assertFalse(physicalDataRoot.exists());
// init crypto fs:
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, "foo");
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
Assert.assertTrue(masterkeyFile.exists());
Assert.assertTrue(masterkeyBkupFile.exists());
fs.create();
@@ -66,7 +67,7 @@ public class CryptoFileSystemTest {
Assert.assertFalse(masterkeyBkupFile.exists());
// first initialization:
new CryptoFileSystem(physicalFs, cryptor, "foo");
new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
Assert.assertTrue(masterkeyBkupFile.exists());
final Instant bkupDateT0 = masterkeyBkupFile.lastModified();
@@ -75,7 +76,7 @@ public class CryptoFileSystemTest {
Thread.sleep(1);
// second initialization:
new CryptoFileSystem(physicalFs, cryptor, "foo");
new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
Assert.assertTrue(masterkeyBkupFile.exists());
final Instant bkupDateT1 = masterkeyBkupFile.lastModified();
@@ -88,7 +89,7 @@ public class CryptoFileSystemTest {
final Cryptor cryptor = new NoCryptor();
final FileSystem physicalFs = new InMemoryFileSystem();
final Folder physicalDataRoot = physicalFs.folder("d");
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, "foo");
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
fs.create();
// add another encrypted folder:
@@ -108,7 +109,7 @@ public class CryptoFileSystemTest {
// mock stuff and prepare crypto FS:
final Cryptor cryptor = new NoCryptor();
final FileSystem physicalFs = new InMemoryFileSystem();
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, "foo");
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
fs.create();
// create foo/bar/ and then move foo/ to baz/:
@@ -131,7 +132,7 @@ public class CryptoFileSystemTest {
// mock stuff and prepare crypto FS:
final Cryptor cryptor = new NoCryptor();
final FileSystem physicalFs = new InMemoryFileSystem();
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, "foo");
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
fs.create();
// create foo/bar/ and then try to move foo/bar/ to foo/
@@ -141,12 +142,12 @@ public class CryptoFileSystemTest {
fooBarFolder.moveTo(fooFolder);
}
@Test(timeout = 10000000)
@Test(timeout = 10000)
public void testWriteAndReadEncryptedFile() {
// mock stuff and prepare crypto FS:
final Cryptor cryptor = new NoCryptor();
final FileSystem physicalFs = new InMemoryFileSystem();
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, "foo");
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
fs.create();
// write test content to file

View File

@@ -30,7 +30,7 @@ public class CryptoReadableFileTest {
}).thenThrow(new UncheckedIOException(new IOException("failed.")));
@SuppressWarnings("resource")
ReadableFile cryptoReadableFile = new CryptoReadableFile(fileContentCryptor, underlyingFile);
ReadableFile cryptoReadableFile = new CryptoReadableFile(fileContentCryptor, underlyingFile, true);
cryptoReadableFile.read(ByteBuffer.allocate(1));
}

View File

@@ -13,9 +13,11 @@ import java.util.List;
import org.cryptomator.crypto.engine.impl.CryptorImpl;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.crypto.CryptoFileSystem;
import org.cryptomator.filesystem.crypto.CryptoFileSystemDelegate;
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
import org.cryptomator.filesystem.invariants.FileSystemFactories.FileSystemFactory;
import org.cryptomator.filesystem.nio.NioFileSystem;
import org.mockito.Mockito;
class FileSystemFactories implements Iterable<FileSystemFactory> {
@@ -48,11 +50,11 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
}
private FileSystem createCryptoFileSystemInMemory() {
return new CryptoFileSystem(createInMemoryFileSystem(), new CryptorImpl(RANDOM_MOCK), "aPassphrase");
return new CryptoFileSystem(createInMemoryFileSystem(), new CryptorImpl(RANDOM_MOCK), Mockito.mock(CryptoFileSystemDelegate.class), "aPassphrase");
}
private FileSystem createCryptoFileSystemNio() {
return new CryptoFileSystem(createNioFileSystem(), new CryptorImpl(RANDOM_MOCK), "aPassphrase");
return new CryptoFileSystem(createNioFileSystem(), new CryptorImpl(RANDOM_MOCK), Mockito.mock(CryptoFileSystemDelegate.class), "aPassphrase");
}
private void add(String name, FileSystemFactory factory) {