mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 18:51:26 +00:00
Using DI to stack up filesystem layers
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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<Cryptor> cryptorProvider;
|
||||
|
||||
@Inject
|
||||
public CryptoFileSystemFactory(Provider<Cryptor> cryptorProvider) {
|
||||
this.cryptorProvider = cryptorProvider;
|
||||
}
|
||||
|
||||
public FileSystem get(Folder root, CharSequence passphrase) {
|
||||
return new BlockAlignedFileSystem(new CryptoFileSystem(root, cryptorProvider.get(), passphrase), FileContentCryptorImpl.CHUNK_SIZE);
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,15 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class CryptoModule {
|
||||
public class CryptoEngineModule {
|
||||
|
||||
@Provides
|
||||
Cryptor provideCryptor(SecureRandom secureRandom) {
|
||||
public Cryptor provideCryptor(SecureRandom secureRandom) {
|
||||
return new CryptorImpl(secureRandom);
|
||||
}
|
||||
|
||||
@Provides
|
||||
SecureRandom provideSecureRandom() {
|
||||
public SecureRandom provideSecureRandom() {
|
||||
try {
|
||||
return SecureRandom.getInstanceStrong();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
@@ -21,7 +21,7 @@ import org.cryptomator.crypto.engine.FileContentEncryptor;
|
||||
public class FileContentCryptorImpl implements FileContentCryptor {
|
||||
|
||||
public static final int CHUNK_SIZE = 32 * 1024;
|
||||
static final int MAC_SIZE = 32;
|
||||
public static final int MAC_SIZE = 32;
|
||||
|
||||
private final SecretKey encryptionKey;
|
||||
private final SecretKey macKey;
|
||||
|
||||
@@ -11,7 +11,7 @@ package org.cryptomator.filesystem.blockaligned;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
|
||||
public class BlockAlignedFileSystem extends BlockAlignedFolder implements FileSystem {
|
||||
class BlockAlignedFileSystem extends BlockAlignedFolder implements FileSystem {
|
||||
|
||||
public BlockAlignedFileSystem(Folder delegate, int blockSize) {
|
||||
super(null, delegate, blockSize);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.cryptomator.filesystem.blockaligned;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.crypto.engine.impl.FileContentCryptorImpl;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
|
||||
@Singleton
|
||||
public class BlockAlignedFileSystemFactory {
|
||||
|
||||
@Inject
|
||||
public BlockAlignedFileSystemFactory() {
|
||||
}
|
||||
|
||||
public FileSystem get(Folder root) {
|
||||
return new BlockAlignedFileSystem(root, FileContentCryptorImpl.CHUNK_SIZE);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
|
||||
public class CryptoFileSystem extends CryptoFolder implements FileSystem {
|
||||
class CryptoFileSystem extends CryptoFolder implements FileSystem {
|
||||
|
||||
private static final String DATA_ROOT_DIR = "d";
|
||||
private static final String ROOT_DIR_FILE = "root";
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.cryptomator.filesystem.crypto;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.crypto.engine.impl.CryptoEngineModule;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = CryptoEngineModule.class)
|
||||
interface CryptoFileSystemComponent {
|
||||
|
||||
CryptoFileSystemFactory cryptoFileSystemFactory();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.cryptomator.filesystem.crypto;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.crypto.engine.Cryptor;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.blockaligned.BlockAlignedFileSystemFactory;
|
||||
import org.cryptomator.filesystem.shortening.ShorteningFileSystemFactory;
|
||||
|
||||
@Singleton
|
||||
public class CryptoFileSystemFactory {
|
||||
|
||||
private final Provider<Cryptor> cryptorProvider;
|
||||
private final ShorteningFileSystemFactory shorteningFileSystemFactory;
|
||||
private final BlockAlignedFileSystemFactory blockAlignedFileSystemFactory;
|
||||
|
||||
@Inject
|
||||
public CryptoFileSystemFactory(Provider<Cryptor> cryptorProvider, ShorteningFileSystemFactory shorteningFileSystemFactory, BlockAlignedFileSystemFactory blockAlignedFileSystemFactory) {
|
||||
this.cryptorProvider = cryptorProvider;
|
||||
this.shorteningFileSystemFactory = shorteningFileSystemFactory;
|
||||
this.blockAlignedFileSystemFactory = blockAlignedFileSystemFactory;
|
||||
}
|
||||
|
||||
public FileSystem get(Folder root, CharSequence passphrase) {
|
||||
final FileSystem nameShorteningFs = shorteningFileSystemFactory.get(root);
|
||||
final FileSystem cryptoFs = new CryptoFileSystem(nameShorteningFs, cryptorProvider.get(), passphrase);
|
||||
return blockAlignedFileSystemFactory.get(cryptoFs);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
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<Node> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.cryptomator.crypto.engine.impl;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Used as drop-in-replacement for {@link CryptoEngineModule} during unit tests.
|
||||
*/
|
||||
public class CryptoEngineTestModule extends CryptoEngineModule {
|
||||
|
||||
@Override
|
||||
public SecureRandom provideSecureRandom() {
|
||||
return new SecureRandom() {
|
||||
|
||||
@Override
|
||||
public void nextBytes(byte[] bytes) {
|
||||
Arrays.fill(bytes, (byte) 0x00);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package org.cryptomator.filesystem.crypto;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.cryptomator.crypto.engine.impl.CryptoEngineTestModule;
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.Node;
|
||||
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 CryptoFileSystemComponentIntegrationTest {
|
||||
|
||||
private static final CryptoFileSystemComponent cryptoFsComp = DaggerCryptoFileSystemComponent.builder().cryptoEngineModule(new CryptoEngineTestModule()).build();
|
||||
|
||||
private FileSystem ciphertextFs;
|
||||
private FileSystem cleartextFs;
|
||||
|
||||
@Before
|
||||
public void setupFileSystems() {
|
||||
ciphertextFs = new InMemoryFileSystem();
|
||||
cleartextFs = cryptoFsComp.cryptoFileSystemFactory().get(ciphertextFs, "TopSecret");
|
||||
cleartextFs.create();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionOfLongFolderNames() {
|
||||
final String shortName = "normal folder name";
|
||||
final String longName = "this will be a long filename after encryption, because its encrypted name is longer than onehundredandeighty characters";
|
||||
|
||||
final Folder shortFolder = cleartextFs.folder(shortName);
|
||||
final Folder longFolder = cleartextFs.folder(longName);
|
||||
|
||||
shortFolder.create();
|
||||
longFolder.create();
|
||||
|
||||
// because of the long file, a metadata folder should exist on the physical layer:
|
||||
Assert.assertEquals(1, ciphertextFs.folder("m").folders().count());
|
||||
Assert.assertTrue(ciphertextFs.folder("m").exists());
|
||||
|
||||
// but the shortened filenames must not be visible on the cleartext layer:
|
||||
Assert.assertArrayEquals(new String[] {shortName, longName}, cleartextFs.folders().map(Node::name).sorted().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionAndDecryptionOfFiles() {
|
||||
// write test content to encrypted file
|
||||
try (WritableFile writable = cleartextFs.file("test1.txt").openWritable()) {
|
||||
writable.write(ByteBuffer.wrap("Hello ".getBytes()));
|
||||
writable.write(ByteBuffer.wrap("World".getBytes()));
|
||||
}
|
||||
|
||||
File physicalFile = ciphertextFs.folder("d").folders().findAny().get().folders().findAny().get().files().findAny().get();
|
||||
Assert.assertTrue(physicalFile.exists());
|
||||
|
||||
// read test content from decrypted file
|
||||
try (ReadableFile readable = cleartextFs.file("test1.txt").openReadable()) {
|
||||
ByteBuffer buf1 = ByteBuffer.allocate(5);
|
||||
readable.read(buf1);
|
||||
buf1.flip();
|
||||
Assert.assertEquals("Hello", new String(buf1.array(), 0, buf1.remaining()));
|
||||
ByteBuffer buf2 = ByteBuffer.allocate(10);
|
||||
readable.read(buf2);
|
||||
buf2.flip();
|
||||
Assert.assertArrayEquals(" World".getBytes(), Arrays.copyOfRange(buf2.array(), 0, buf2.remaining()));
|
||||
}
|
||||
}
|
||||
|
||||
@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); // "random access", told you so.
|
||||
readable.read(buf);
|
||||
buf.flip();
|
||||
Assert.assertEquals(i, buf.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +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.filesystem.crypto;
|
||||
|
||||
import static org.cryptomator.filesystem.FileSystemVisitor.fileSystemVisitor;
|
||||
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
|
||||
public final class DirectoryPrinter {
|
||||
|
||||
private DirectoryPrinter() {
|
||||
}
|
||||
|
||||
public static String print(Folder rootFolder) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
StringBuilder indentation = new StringBuilder();
|
||||
fileSystemVisitor() //
|
||||
.beforeFolder(folder -> {
|
||||
result.append(indentation).append(folder.name()).append("/\n");
|
||||
indentation.append(" ");
|
||||
}) //
|
||||
.afterFolder(folder -> {
|
||||
indentation.delete(indentation.length() - 2, indentation.length());
|
||||
}).forEachFile(file -> {
|
||||
result.append(indentation).append(file.name()).append('\n');
|
||||
}) //
|
||||
.visit(rootFolder);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +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.filesystem.crypto;
|
||||
|
||||
import static org.cryptomator.filesystem.FileSystemVisitor.fileSystemVisitor;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.cryptomator.crypto.engine.Cryptor;
|
||||
import org.cryptomator.crypto.engine.impl.TestCryptorImplFactory;
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.Node;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.cryptomator.filesystem.blacklisting.BlacklistingFileSystem;
|
||||
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
|
||||
import org.cryptomator.filesystem.shortening.ShorteningFileSystem;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class EncryptAndShortenIntegrationTest {
|
||||
|
||||
// private static final Logger LOG = LoggerFactory.getLogger(EncryptAndShortenIntegrationTest.class);
|
||||
|
||||
@Test
|
||||
public void testEncryptionOfLongFolderNames() {
|
||||
final FileSystem physicalFs = new InMemoryFileSystem();
|
||||
final Predicate<Node> 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);
|
||||
|
||||
final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
|
||||
cryptor.randomizeMasterkey();
|
||||
final FileSystem fs = new CryptoFileSystem(shorteningFs, cryptor, "foo");
|
||||
fs.create();
|
||||
final Folder shortFolder = fs.folder("normal folder name");
|
||||
shortFolder.create();
|
||||
final Folder longFolder = fs.folder("this will be a long filename after encryption");
|
||||
longFolder.create();
|
||||
|
||||
// on the first (physical) layer all files including metadata files are visible:
|
||||
// the long name will produce a metadata file on the physical layer:
|
||||
// LOG.debug("Physical file system:\n" + DirectoryPrinter.print(physicalFs));
|
||||
Assert.assertEquals(1, physicalFs.folder("m").folders().count());
|
||||
Assert.assertTrue(physicalFs.folder("m").exists());
|
||||
|
||||
// on the second (blacklisting) layer we hide the metadata folder:
|
||||
// LOG.debug("Filtered files:\n" + DirectoryPrinter.print(metadataHidingFs));
|
||||
Assert.assertEquals(1, metadataHidingFs.folders().count()); // only "d", no "m".
|
||||
|
||||
// on the third layer all .lng files are resolved to their actual names:
|
||||
// LOG.debug("Unlimited filename length:\n" + DirectoryPrinter.print(shorteningFs));
|
||||
fileSystemVisitor() //
|
||||
.forEachNode(node -> {
|
||||
Assert.assertFalse(node.name().endsWith(".lng"));
|
||||
}) //
|
||||
.visit(shorteningFs);
|
||||
|
||||
// on the fourth (cleartext) layer we have cleartext names on the root level:
|
||||
// LOG.debug("Cleartext files:\n" + DirectoryPrinter.print(fs));
|
||||
Assert.assertArrayEquals(new String[] {"normal folder name", "this will be a long filename after encryption"}, fs.folders().map(Node::name).sorted().toArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionAndDecryptionOfFiles() {
|
||||
final FileSystem physicalFs = new InMemoryFileSystem();
|
||||
final FileSystem shorteningFs = new ShorteningFileSystem(physicalFs, physicalFs.folder("m"), 70);
|
||||
final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
|
||||
cryptor.randomizeMasterkey();
|
||||
final FileSystem fs = new CryptoFileSystem(shorteningFs, cryptor, "foo");
|
||||
fs.create();
|
||||
|
||||
// write test content to encrypted file
|
||||
try (WritableFile writable = fs.file("test1.txt").openWritable()) {
|
||||
writable.write(ByteBuffer.wrap("Hello ".getBytes()));
|
||||
writable.write(ByteBuffer.wrap("World".getBytes()));
|
||||
}
|
||||
|
||||
File physicalFile = physicalFs.folder("d").folders().findAny().get().folders().findAny().get().files().findAny().get();
|
||||
Assert.assertTrue(physicalFile.exists());
|
||||
|
||||
// read test content from decrypted file
|
||||
try (ReadableFile readable = fs.file("test1.txt").openReadable()) {
|
||||
ByteBuffer buf1 = ByteBuffer.allocate(5);
|
||||
readable.read(buf1);
|
||||
buf1.flip();
|
||||
Assert.assertEquals("Hello", new String(buf1.array(), 0, buf1.remaining()));
|
||||
ByteBuffer buf2 = ByteBuffer.allocate(10);
|
||||
readable.read(buf2);
|
||||
buf2.flip();
|
||||
Assert.assertArrayEquals(" World".getBytes(), Arrays.copyOfRange(buf2.array(), 0, buf2.remaining()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,17 @@
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DI -->
|
||||
<dependency>
|
||||
<groupId>com.google.dagger</groupId>
|
||||
<artifactId>dagger</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.dagger</groupId>
|
||||
<artifactId>dagger-compiler</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Tests -->
|
||||
<dependency>
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.Node;
|
||||
|
||||
public class BlacklistingFileSystem extends BlacklistingFolder implements FileSystem {
|
||||
class BlacklistingFileSystem extends BlacklistingFolder implements FileSystem {
|
||||
|
||||
public BlacklistingFileSystem(Folder root, Predicate<Node> hiddenNodes) {
|
||||
super(null, root, hiddenNodes);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.cryptomator.filesystem.blacklisting;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.Node;
|
||||
|
||||
@Singleton
|
||||
public class BlacklistingFileSystemFactory {
|
||||
|
||||
@Inject
|
||||
public BlacklistingFileSystemFactory() {
|
||||
}
|
||||
|
||||
public FileSystem get(Folder root, Predicate<Node> hiddenFiles) {
|
||||
return new BlacklistingFileSystem(root, hiddenFiles);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package org.cryptomator.filesystem.shortening;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
|
||||
public class ShorteningFileSystem extends ShorteningFolder implements FileSystem {
|
||||
class ShorteningFileSystem extends ShorteningFolder implements FileSystem {
|
||||
|
||||
public ShorteningFileSystem(Folder root, Folder metadataRoot, int threshold) {
|
||||
super(null, root, "", new FilenameShortener(metadataRoot, threshold));
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.cryptomator.filesystem.shortening;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.Node;
|
||||
import org.cryptomator.filesystem.blacklisting.BlacklistingFileSystemFactory;
|
||||
|
||||
@Singleton
|
||||
public class ShorteningFileSystemFactory {
|
||||
|
||||
private static final int SHORTENING_THRESHOLD = 140;
|
||||
private static final String METADATA_FOLDER_NAME = "m";
|
||||
|
||||
private final BlacklistingFileSystemFactory blacklistingFileSystemFactory;
|
||||
|
||||
@Inject
|
||||
public ShorteningFileSystemFactory(BlacklistingFileSystemFactory blacklistingFileSystemFactory) {
|
||||
this.blacklistingFileSystemFactory = blacklistingFileSystemFactory;
|
||||
}
|
||||
|
||||
public FileSystem get(Folder root) {
|
||||
final Folder metadataFolder = root.folder(METADATA_FOLDER_NAME);
|
||||
final Predicate<Node> isMetadataFolder = (Node node) -> metadataFolder.equals(node);
|
||||
final FileSystem metadataHidingFs = blacklistingFileSystemFactory.get(root, isMetadataFolder);
|
||||
return new ShorteningFileSystem(metadataHidingFs, metadataFolder, SHORTENING_THRESHOLD);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.cryptomator.filesystem.shortening;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
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 ShorteningFileSystemFactoryTest {
|
||||
|
||||
private static final ShorteningFileSystemFactory shorteningFsFactory = DaggerShorteningFileSystemTestComponent.create().shorteningFileSystemFactory();
|
||||
private static final String LONG_NAME = "aaaaabbbbbcccccdddddeeeeefffffggggghhhhhiiiiijjjjjkkkkklllll" //
|
||||
+ "mmmmmnnnnnooooopppppqqqqqrrrrrssssstttttuuuuuvvvvvwwwwwxxxxxyyyyyzzzzz" //
|
||||
+ "00000111112222233333444445555566666777778888899999";
|
||||
private static final String SHORTENED_FILE = "UYPJJ35VGP2JJ4YISC5S2XQLENLR5MVC.lng"; // base32(sha1(LONG_NAME)) + '.lng'
|
||||
private static final String CORRESPONDING_METADATA_FILE = "m/UY/PJ/UYPJJ35VGP2JJ4YISC5S2XQLENLR5MVC.lng";
|
||||
|
||||
private FileSystem physicalFs;
|
||||
private FileSystem shortenedFs;
|
||||
|
||||
@Before
|
||||
public void setupFileSystems() {
|
||||
physicalFs = new InMemoryFileSystem();
|
||||
shortenedFs = shorteningFsFactory.get(physicalFs);
|
||||
shortenedFs.create();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileCreation() {
|
||||
File file = shortenedFs.file(LONG_NAME);
|
||||
Assert.assertFalse(file.exists());
|
||||
Assert.assertFalse(physicalFs.resolveFile(SHORTENED_FILE).exists());
|
||||
Assert.assertFalse(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
|
||||
try (WritableFile w = file.openWritable()) {
|
||||
w.write(ByteBuffer.allocate(0));
|
||||
}
|
||||
Assert.assertTrue(file.exists());
|
||||
Assert.assertTrue(physicalFs.resolveFile(SHORTENED_FILE).exists());
|
||||
Assert.assertTrue(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFolderCreation() {
|
||||
Folder folder = shortenedFs.folder(LONG_NAME);
|
||||
Assert.assertFalse(folder.exists());
|
||||
Assert.assertFalse(physicalFs.resolveFolder(SHORTENED_FILE).exists());
|
||||
Assert.assertFalse(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
|
||||
folder.create();
|
||||
Assert.assertTrue(folder.exists());
|
||||
Assert.assertTrue(physicalFs.resolveFolder(SHORTENED_FILE).exists());
|
||||
Assert.assertTrue(physicalFs.resolveFile(CORRESPONDING_METADATA_FILE).exists());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.filesystem.shortening;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component
|
||||
public interface ShorteningFileSystemTestComponent {
|
||||
|
||||
ShorteningFileSystemFactory shorteningFileSystemFactory();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user