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 f12d04cf7..cb94a12fa 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
@@ -21,6 +21,14 @@ public interface FilenameCryptor {
*/
String hashDirectoryId(String cleartextDirectoryId);
+ /**
+ * Tests without an actual decryption attempt, if a name is a well-formed ciphertext.
+ *
+ * @param ciphertextName Filename in question
+ * @return true if the given name is likely to be a valid ciphertext
+ */
+ boolean isEncryptedFilename(String ciphertextName);
+
/**
* @param cleartextName original filename including cleartext file extension
* @param associatedData optional associated data, that will not get encrypted but needs to be provided during decryption
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 3469ac554..5db611548 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
@@ -49,6 +49,11 @@ class FilenameCryptorImpl implements FilenameCryptor {
return BASE32.encodeAsString(hashedBytes);
}
+ @Override
+ public boolean isEncryptedFilename(String ciphertextName) {
+ return BASE32.isInAlphabet(ciphertextName);
+ }
+
@Override
public String encryptFilename(String cleartextName, byte[]... associatedData) {
final byte[] cleartextBytes = cleartextName.getBytes(UTF_8);
diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java
index fb1cfe803..e8c4aa9b6 100644
--- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java
+++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFile.java
@@ -21,8 +21,6 @@ import org.cryptomator.filesystem.WritableFile;
public class CryptoFile extends CryptoNode implements File {
- static final String FILE_EXT = ".file";
-
public CryptoFile(CryptoFolder parent, String name, Cryptor cryptor) {
super(parent, name, cryptor);
}
@@ -30,7 +28,7 @@ public class CryptoFile extends CryptoNode implements File {
@Override
protected String encryptedName() {
final byte[] parentDirId = parent.getDirectoryId().getBytes(UTF_8);
- return cryptor.getFilenameCryptor().encryptFilename(name(), parentDirId) + FILE_EXT;
+ return cryptor.getFilenameCryptor().encryptFilename(name(), parentDirId);
}
@Override
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 151b96195..d67a2ff3a 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
@@ -19,6 +19,7 @@ import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
import java.util.stream.Stream;
import org.apache.commons.io.IOUtils;
@@ -33,7 +34,7 @@ import org.cryptomator.filesystem.WritableFile;
class CryptoFolder extends CryptoNode implements Folder {
- static final String DIR_EXT = ".dir";
+ static final String DIR_SUFFIX = "_";
private final WeakValuedCache folders = WeakValuedCache.usingLoader(this::newFolder);
private final WeakValuedCache files = WeakValuedCache.usingLoader(this::newFile);
@@ -46,7 +47,7 @@ class CryptoFolder extends CryptoNode implements Folder {
@Override
protected String encryptedName() {
final byte[] parentDirId = parent().map(CryptoFolder::getDirectoryId).map(s -> s.getBytes(UTF_8)).orElse(null);
- return cryptor.getFilenameCryptor().encryptFilename(name(), parentDirId) + DIR_EXT;
+ return cryptor.getFilenameCryptor().encryptFilename(name(), parentDirId) + DIR_SUFFIX;
}
Folder physicalFolder() {
@@ -82,13 +83,16 @@ class CryptoFolder extends CryptoNode implements Folder {
@Override
public Stream files() {
- return physicalFolder().files().map(File::name).filter(s -> s.endsWith(CryptoFile.FILE_EXT)).map(this::decryptChildFileName).map(this::file);
+ return physicalFolder().files().map(File::name).filter(isEncryptedFileName()).map(this::decryptChildFileName).map(this::file);
+ }
+
+ private Predicate isEncryptedFileName() {
+ return (String name) -> cryptor.getFilenameCryptor().isEncryptedFilename(name);
}
private String decryptChildFileName(String encryptedFileName) {
final byte[] dirId = getDirectoryId().getBytes(UTF_8);
- final String ciphertext = StringUtils.removeEnd(encryptedFileName, CryptoFile.FILE_EXT);
- return cryptor.getFilenameCryptor().decryptFilename(ciphertext, dirId);
+ return cryptor.getFilenameCryptor().decryptFilename(encryptedFileName, dirId);
}
@Override
@@ -102,12 +106,16 @@ class CryptoFolder extends CryptoNode implements Folder {
@Override
public Stream folders() {
- return physicalFolder().files().map(File::name).filter(s -> s.endsWith(CryptoFolder.DIR_EXT)).map(this::decryptChildFolderName).map(this::folder);
+ return physicalFolder().files().map(File::name).filter(isEncryptedDirectoryName()).map(this::decryptChildFolderName).map(this::folder);
+ }
+
+ private Predicate isEncryptedDirectoryName() {
+ return (String name) -> name.endsWith(DIR_SUFFIX) && isEncryptedFileName().test(StringUtils.removeEnd(name, DIR_SUFFIX));
}
private String decryptChildFolderName(String encryptedFolderName) {
final byte[] dirId = getDirectoryId().getBytes(UTF_8);
- final String ciphertext = StringUtils.removeEnd(encryptedFolderName, CryptoFolder.DIR_EXT);
+ final String ciphertext = StringUtils.removeEnd(encryptedFolderName, CryptoFolder.DIR_SUFFIX);
return cryptor.getFilenameCryptor().decryptFilename(ciphertext, dirId);
}
diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFilenameCryptor.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFilenameCryptor.java
index 0788643bc..9f3901f7e 100644
--- a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFilenameCryptor.java
+++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/NoFilenameCryptor.java
@@ -28,6 +28,11 @@ class NoFilenameCryptor implements FilenameCryptor {
return BASE32.encodeAsString(hashedBytes);
}
+ @Override
+ public boolean isEncryptedFilename(String ciphertextName) {
+ return true;
+ }
+
@Override
public String encryptFilename(String cleartextName, byte[]... associatedData) {
return cleartextName;
diff --git a/main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactory.java b/main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactory.java
index 65073e763..4eb83cbc7 100644
--- a/main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactory.java
+++ b/main/filesystem-nameshortening/src/main/java/org/cryptomator/filesystem/shortening/ShorteningFileSystemFactory.java
@@ -19,7 +19,7 @@ import org.cryptomator.filesystem.blacklisting.SamePathPredicate;
@Singleton
public class ShorteningFileSystemFactory {
- private static final int SHORTENING_THRESHOLD = 140;
+ private static final int SHORTENING_THRESHOLD = 129; // 128 + "_"
private static final String METADATA_FOLDER_NAME = "m";
private final BlacklistingFileSystemFactory blacklistingFileSystemFactory;