diff --git a/azure/pom.xml b/azure/pom.xml
index ae4a2a6a1fd..1e0d96a282d 100644
--- a/azure/pom.xml
+++ b/azure/pom.xml
@@ -18,7 +18,7 @@
ch.cyberduck
parent
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
azure
diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java
index cd7876e88ee..9e8af2507f4 100644
--- a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java
@@ -21,7 +21,7 @@
import ch.cyberduck.core.DisabledProgressListener;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.LoginConnectionService;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.VaultTest;
import org.junit.After;
@@ -34,11 +34,11 @@ public class AbstractAzureTest extends VaultTest {
@Parameterized.Parameters(name = "vaultVersion = {0}")
public static Object[] data() {
- return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+ return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
}
@Parameterized.Parameter
- public int vaultVersion;
+ public VaultMetadata.Type vaultVersion;
@After
public void disconnect() throws Exception {
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java
index 72f478660b3..eac1c2205c0 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java
@@ -31,8 +31,10 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -42,7 +44,6 @@
import java.util.EnumSet;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -51,9 +52,9 @@ public class AzureDirectoryFeatureTest extends AbstractAzureTest {
@Test
public void testMakeDirectoryEncrypted() throws Exception {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
@@ -62,13 +63,15 @@ public void testMakeDirectoryEncrypted() throws Exception {
cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
+
+ //TODO enable
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testMakeDirectoryLongFilenameEncrypted() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java
index 174e34b2600..994d385f037 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java
@@ -27,11 +27,13 @@
import ch.cyberduck.core.cryptomator.features.CryptoListService;
import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
+import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.shared.DefaultTouchFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.junit.Test;
@@ -52,9 +54,9 @@ public class AzureListServiceTest extends AbstractAzureTest {
@Test
public void testListCryptomator() throws Exception {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
assertTrue(new CryptoListService(session, new AzureObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java
index 3aa606d75fc..35a553bdf02 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java
@@ -38,6 +38,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.junit.Test;
@@ -58,9 +59,9 @@ public class AzureMoveFeatureTest extends AbstractAzureTest {
@Test
public void testMove() throws Exception {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path folder = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new AzureWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java
index b8a257cd4df..a6952a94854 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java
@@ -32,8 +32,10 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -44,19 +46,20 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
public class AzureTouchFeatureTest extends AbstractAzureTest {
+ //TODO
+
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testTouchLongFilenameEncrypted() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
@@ -68,12 +71,12 @@ public void testTouchLongFilenameEncrypted() throws Exception {
}
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testTouchLongFilenameEncryptedDefaultFeature() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), cryptomator).touch(
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java
index a11d3355b97..11c66b56332 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java
@@ -38,6 +38,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.lang3.RandomUtils;
@@ -66,9 +67,9 @@ public void testWrite() throws Exception {
final byte[] content = RandomUtils.nextBytes(1048576);
status.setLength(content.length);
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final CryptoWriteFeature writer = new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator);
final FileHeader header = cryptomator.getFileHeaderCryptor().create();
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
index 1e3bc02b566..459a0c75a3b 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
@@ -47,12 +47,14 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.CopyWorker;
import ch.cyberduck.core.worker.DeleteWorker;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomUtils;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -76,8 +78,8 @@ public void testCopyFile() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final byte[] content = RandomUtils.nextBytes(40500);
@@ -102,8 +104,8 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
@@ -120,11 +122,14 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session);
}
+ //TODO
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
@@ -147,8 +152,9 @@ public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception
@Test
public void testCopyFolder() throws Exception {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
@@ -185,8 +191,8 @@ public void testCopyFileIntoVault() throws Exception {
assertTrue(new AzureFindFeature(session).find(cleartextFile));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
@@ -214,8 +220,8 @@ public void testCopyDirectoryIntoVault() throws Exception {
new AzureTouchFeature(session).touch(new AzureWriteFeature(session), cleartextFile, new TransferStatus());
assertTrue(new AzureFindFeature(session).find(cleartextFolder));
assertTrue(new AzureFindFeature(session).find(cleartextFile));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
// move directory into vault
@@ -239,8 +245,8 @@ public void testCopyFileOutsideVault() throws Exception {
new AzureDirectoryFeature(session).mkdir(new AzureWriteFeature(session), clearFolder, new TransferStatus());
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
@@ -265,8 +271,8 @@ public void testCopyDirectoryOutsideVault() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(
diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java
index 4ac361ed8c5..008e34a8fd6 100644
--- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java
+++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java
@@ -51,6 +51,7 @@
import ch.cyberduck.core.transfer.UploadTransfer;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.SingleTransferWorker;
import ch.cyberduck.test.IntegrationTest;
@@ -77,8 +78,9 @@ public class CryptoAzureSingleTransferWorkerTest extends AbstractAzureTest {
@Test
public void testUpload() throws Exception {
final Path home = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path dir1 = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.placeholder));
final Local localDirectory1 = new Local(System.getProperty("java.io.tmpdir"), new AlphanumericRandomStringService().random());
diff --git a/backblaze/pom.xml b/backblaze/pom.xml
index 170eeec3f8b..942aa94a055 100644
--- a/backblaze/pom.xml
+++ b/backblaze/pom.xml
@@ -19,7 +19,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
backblaze
jar
diff --git a/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java b/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java
index 898fdd7c710..fa8c3f533ed 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/b2/AbstractB2Test.java
@@ -26,10 +26,10 @@
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.Profile;
import ch.cyberduck.core.ProtocolFactory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.VaultTest;
import org.junit.After;
@@ -47,11 +47,11 @@ public class AbstractB2Test extends VaultTest {
@Parameterized.Parameters(name = "vaultVersion = {0}")
public static Object[] data() {
- return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+ return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
}
@Parameterized.Parameter
- public int vaultVersion;
+ public VaultMetadata.Type vaultVersion;
@After
public void disconnect() throws Exception {
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java
index c3207396c44..5dd9b60c437 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2DirectoryFeatureTest.java
@@ -39,8 +39,10 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -52,7 +54,6 @@
import java.util.stream.Collectors;
import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -61,9 +62,9 @@ public class B2DirectoryFeatureTest extends AbstractB2Test {
@Test
public void testMakeDirectoryEncrypted() throws Exception {
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path test = cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -82,12 +83,12 @@ public void testMakeDirectoryEncrypted() throws Exception {
}
@Test
+ @Ignore("Filename shortening not yet implemented")
public void testMakeDirectoryLongFilenameEncrypted() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final Path test = cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java
index 868c5e95afb..197fa82e616 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadServiceTest.java
@@ -46,6 +46,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
@@ -76,9 +77,9 @@ public class B2LargeUploadServiceTest extends AbstractB2Test {
public void testWrite() throws Exception {
// 5L * 1024L * 1024L
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final CryptoUploadFeature service = new CryptoUploadFeature<>(session,
@@ -111,9 +112,9 @@ public void testWrite() throws Exception {
public void testUploadWithBulk() throws Exception {
// 5L * 1024L * 1024L
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus writeStatus = new TransferStatus();
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java
index 664d372a3f5..1a332aba815 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2LargeUploadWriteFeatureTest.java
@@ -35,6 +35,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
@@ -61,9 +62,9 @@ public class B2LargeUploadWriteFeatureTest extends AbstractB2Test {
@Test
public void testWrite() throws Exception {
final Path container = new Path("test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new B2LargeUploadWriteFeature(session, fileid), cryptomator);
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java
index 4e67fcf8ad0..9b5c0987f7b 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2ListServiceTest.java
@@ -34,6 +34,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.junit.Test;
@@ -56,9 +57,9 @@ public class B2ListServiceTest extends AbstractB2Test {
@Test
public void testListCryptomator() throws Exception {
final Path home = new Path("test-cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
assertTrue(new CryptoListService(session, new B2ListService(session, fileid), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java
index c2744bcfc23..a8fa2ab5208 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2TouchFeatureTest.java
@@ -36,8 +36,10 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -50,7 +52,6 @@
import synapticloop.b2.response.BaseB2Response;
import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -59,9 +60,9 @@ public class B2TouchFeatureTest extends AbstractB2Test {
@Test
public void testTouchEncrypted() throws Exception {
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final TransferStatus status = new TransferStatus();
@@ -76,12 +77,12 @@ public void testTouchEncrypted() throws Exception {
}
@Test
+ @Ignore("Filename shortening not implemented yet")
public void testTouchLongFilenameEncrypted() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final TransferStatus status = new TransferStatus();
@@ -96,9 +97,9 @@ public void testTouchLongFilenameEncrypted() throws Exception {
@Test
public void testTouchEncryptedDefaultFeature() throws Exception {
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
final TransferStatus status = new TransferStatus();
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java
index 7713007577b..b955c1c504f 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/B2WriteFeatureTest.java
@@ -38,6 +38,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.lang3.RandomUtils;
@@ -69,9 +70,9 @@ public void testWrite() throws Exception {
final byte[] content = RandomUtils.nextBytes(length);
status.setLength(content.length);
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
index d54dede8408..a00d8c836e7 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
@@ -46,12 +46,14 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.CopyWorker;
import ch.cyberduck.core.worker.DeleteWorker;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomUtils;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -66,7 +68,6 @@
import synapticloop.b2.response.BaseB2Response;
import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -78,8 +79,8 @@ public void testCopyFile() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final byte[] content = RandomUtils.nextBytes(40500);
@@ -105,8 +106,8 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
@@ -125,15 +126,15 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
}
@Test
+ @Ignore("Filename shortening not implemented yet")
public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("/test-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume));
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
@@ -157,8 +158,8 @@ public void testCopyFolder() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
@@ -194,8 +195,8 @@ public void testCopyFileIntoVault() throws Exception {
assertTrue(new B2FindFeature(session, fileid).find(cleartextFile));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -221,8 +222,8 @@ public void testCopyDirectoryIntoVault() throws Exception {
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), cleartextFile, new TransferStatus());
assertTrue(new B2FindFeature(session, fileid).find(cleartextFolder));
assertTrue(new B2FindFeature(session, fileid).find(cleartextFile));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
// move directory into vault
@@ -247,8 +248,8 @@ public void testCopyFileOutsideVault() throws Exception {
new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), clearFolder, new TransferStatus());
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -273,8 +274,8 @@ public void testCopyDirectoryOutsideVault() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java
index a061a4203d2..64df31ff4ae 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/CryptoB2SingleTransferWorkerTest.java
@@ -50,6 +50,7 @@
import ch.cyberduck.core.transfer.UploadTransfer;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.SingleTransferWorker;
import ch.cyberduck.test.IntegrationTest;
@@ -91,8 +92,8 @@ public void testUpload() throws Exception {
final OutputStream out2 = localFile2.getOutputStream(false);
IOUtils.write(content, out2);
out2.close();
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Transfer t = new UploadTransfer(new Host(new TestProtocol()), Collections.singletonList(new TransferItem(dir1, localDirectory1)), new NullFilter<>());
assertTrue(new SingleTransferWorker(session, session, t, new TransferOptions(), new TransferSpeedometer(t), new DisabledTransferPrompt() {
diff --git a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
index 93b8af69087..78bb4a4f431 100644
--- a/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
+++ b/backblaze/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
@@ -40,6 +40,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.MoveWorker;
import ch.cyberduck.test.IntegrationTest;
@@ -68,8 +69,8 @@ public void testMoveFileIntoVault() throws Exception {
assertTrue(new DefaultFindFeature(session).find(clearFile));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -95,8 +96,8 @@ public void testMoveDirectoryIntoVault() throws Exception {
new B2TouchFeature(session, fileid).touch(new B2WriteFeature(session, fileid), clearFile, new TransferStatus());
assertTrue(new DefaultFindFeature(session).find(clearFolder));
assertTrue(new DefaultFindFeature(session).find(clearFile));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
// move directory into vault
@@ -121,8 +122,8 @@ public void testMoveFileOutsideVault() throws Exception {
new B2DirectoryFeature(session, fileid).mkdir(new B2WriteFeature(session, fileid), clearFolder, new TransferStatus());
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new B2DirectoryFeature(session, fileid)).mkdir(
@@ -148,8 +149,8 @@ public void testMoveDirectoryOutsideVault() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
diff --git a/binding/pom.xml b/binding/pom.xml
index ee6406562ff..e15d03cf9de 100644
--- a/binding/pom.xml
+++ b/binding/pom.xml
@@ -19,7 +19,7 @@
ch.cyberduck
parent
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
binding
jar
diff --git a/bonjour/dll/pom.xml b/bonjour/dll/pom.xml
index 80b55d2576d..4fa93806c42 100644
--- a/bonjour/dll/pom.xml
+++ b/bonjour/dll/pom.xml
@@ -5,7 +5,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
Cyberduck.Bonjour
pom
diff --git a/bonjour/native/pom.xml b/bonjour/native/pom.xml
index 7f28e0b32f9..37e30fadc52 100644
--- a/bonjour/native/pom.xml
+++ b/bonjour/native/pom.xml
@@ -5,7 +5,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
Cyberduck.Bonjour.Native
pom
diff --git a/bonjour/pom.xml b/bonjour/pom.xml
index ec313109d6e..2c27a2ccb1b 100644
--- a/bonjour/pom.xml
+++ b/bonjour/pom.xml
@@ -18,7 +18,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
4.0.0
diff --git a/box/pom.xml b/box/pom.xml
index 2e75ca42111..da509e71996 100644
--- a/box/pom.xml
+++ b/box/pom.xml
@@ -19,7 +19,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
box
diff --git a/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java b/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java
index 5ece290f673..d3ba73eb22f 100644
--- a/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java
+++ b/box/src/test/java/ch/cyberduck/core/box/AbstractBoxTest.java
@@ -27,10 +27,10 @@
import ch.cyberduck.core.Profile;
import ch.cyberduck.core.ProtocolFactory;
import ch.cyberduck.core.Scheme;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.VaultTest;
import org.junit.After;
@@ -48,11 +48,11 @@ public class AbstractBoxTest extends VaultTest {
@Parameterized.Parameters(name = "vaultVersion = {0}")
public static Object[] data() {
- return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+ return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
}
@Parameterized.Parameter
- public int vaultVersion;
+ public VaultMetadata.Type vaultVersion;
@After
public void disconnect() throws Exception {
diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java
index 1c604e5f503..e83b706d8af 100644
--- a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java
+++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxThresholdUploadServiceTest.java
@@ -47,6 +47,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
@@ -77,8 +78,8 @@ public void testUploadVaultWithBulkFeature() throws Exception {
final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L));
final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java
index daa3c8cec1a..eceb543152e 100644
--- a/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java
+++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BoxWriteFeatureTest.java
@@ -38,6 +38,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
@@ -66,8 +67,8 @@ public void testWriteVault() throws Exception {
final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new BoxWriteFeature(session, fileid), cryptomator);
final byte[] content = RandomUtils.nextBytes(6 * 1024 * 1024);
@@ -99,8 +100,8 @@ public void testWriteVaultWithTimeStamp() throws Exception {
final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new BoxWriteFeature(session, fileid), cryptomator);
final byte[] content = RandomUtils.nextBytes(6 * 1024 * 1024);
diff --git a/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java b/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java
index 0f36353221c..4eedbd39489 100644
--- a/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java
+++ b/box/src/test/java/ch/cyberduck/core/cryptomator/BufferWriteFeatureTest.java
@@ -39,6 +39,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
@@ -66,8 +67,8 @@ public void testWriteVault() throws Exception {
final Path container = new BoxDirectoryFeature(session, fileid).mkdir(new BoxWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final CryptoWriteFeature feature = new CryptoWriteFeature<>(session, new BufferWriteFeature(session), cryptomator);
final byte[] content = RandomUtils.nextBytes(1024 * 1024);
diff --git a/brick/pom.xml b/brick/pom.xml
index c5ef41a7455..3a636158dd8 100644
--- a/brick/pom.xml
+++ b/brick/pom.xml
@@ -18,7 +18,7 @@
ch.cyberduck
parent
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
brick
jar
diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java
index b1ea3afff2c..394d54f73a6 100644
--- a/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java
+++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickCopyFeature.java
@@ -62,7 +62,7 @@ public Path copy(final Path file, final Path target, final TransferStatus status
if(entity.getFileMigrationId() != null) {
this.poll(client, entity);
}
- return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVault(null));
+ return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVaultMetadata(null));
}
catch(ApiException e) {
throw new BrickExceptionMappingService().map("Cannot copy {0}", e, file);
diff --git a/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java b/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java
index c4d356a8c6c..aa7d48d8183 100644
--- a/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java
+++ b/brick/src/main/java/ch/cyberduck/core/brick/BrickMoveFeature.java
@@ -60,7 +60,7 @@ public Path move(final Path file, final Path target, final TransferStatus status
if(entity.getFileMigrationId() != null) {
this.poll(client, entity);
}
- return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVault(null));
+ return new Path(target).withAttributes(new PathAttributes(file.attributes()).setVaultMetadata(null));
}
catch(ApiException e) {
throw new BrickExceptionMappingService().map("Cannot rename {0}", e, file);
diff --git a/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java b/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java
index 119ddc004c6..7f5ac86fdfb 100644
--- a/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java
+++ b/brick/src/test/java/ch/cyberduck/core/brick/AbstractBrickTest.java
@@ -26,10 +26,10 @@
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.Profile;
import ch.cyberduck.core.ProtocolFactory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.VaultTest;
import org.junit.After;
@@ -47,11 +47,11 @@ public class AbstractBrickTest extends VaultTest {
@Parameterized.Parameters(name = "vaultVersion = {0}")
public static Object[] data() {
- return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+ return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
}
@Parameterized.Parameter
- public int vaultVersion;
+ public VaultMetadata.Type vaultVersion;
@After
public void disconnect() throws Exception {
diff --git a/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java b/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java
index 54f85e1b235..7c63c8e6176 100644
--- a/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java
+++ b/brick/src/test/java/ch/cyberduck/core/brick/cryptomator/BrickListServiceTest.java
@@ -26,7 +26,8 @@
import ch.cyberduck.core.brick.BrickListService;
import ch.cyberduck.core.brick.BrickWriteFeature;
import ch.cyberduck.core.brick.io.swagger.client.model.FileEntity;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.CryptoVaultProvider;
import ch.cyberduck.core.cryptomator.features.CryptoListService;
import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature;
import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
@@ -35,6 +36,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.junit.Assert;
@@ -58,8 +60,8 @@ public void testListCryptomator() throws Exception {
EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
assertTrue(new CryptoListService(session, new BrickListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
diff --git a/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java b/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java
index 6e518e70b37..3d96f051ce2 100644
--- a/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java
+++ b/brick/src/test/java/ch/cyberduck/core/cryptomator/DefaultTouchFeatureTest.java
@@ -33,8 +33,10 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -45,19 +47,20 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
public class DefaultTouchFeatureTest extends AbstractBrickTest {
+ //TODO
+
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testTouchLongFilenameEncrypted() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
@@ -68,12 +71,12 @@ public void testTouchLongFilenameEncrypted() throws Exception {
}
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testTouchLongFilenameEncryptedDefaultFeature() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume));
- final CryptoVault cryptomator = new CryptoVault(
- new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)));
- final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
diff --git a/cli/dll/pom.xml b/cli/dll/pom.xml
index 045f5dc5e3b..2a297b94fdf 100644
--- a/cli/dll/pom.xml
+++ b/cli/dll/pom.xml
@@ -20,7 +20,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
Cyberduck.Cli
pom
diff --git a/cli/linux/pom.xml b/cli/linux/pom.xml
index 2f8896ea363..a97ffa4f530 100644
--- a/cli/linux/pom.xml
+++ b/cli/linux/pom.xml
@@ -20,7 +20,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
cli-linux
Cyberduck CLI Linux
diff --git a/cli/osx/pom.xml b/cli/osx/pom.xml
index 8c4769b818f..e0f71129c67 100644
--- a/cli/osx/pom.xml
+++ b/cli/osx/pom.xml
@@ -20,7 +20,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
cli-osx
Cyberduck CLI Mac
diff --git a/cli/pom.xml b/cli/pom.xml
index 8a75fa3aa1b..43316cca528 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -19,7 +19,7 @@
ch.cyberduck
parent
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
cli
diff --git a/cli/src/main/csharp/ch/cyberduck/cli/WindowsTerminalPreferences.cs b/cli/src/main/csharp/ch/cyberduck/cli/WindowsTerminalPreferences.cs
index e4cadaf6cb6..f6c59c0ab32 100644
--- a/cli/src/main/csharp/ch/cyberduck/cli/WindowsTerminalPreferences.cs
+++ b/cli/src/main/csharp/ch/cyberduck/cli/WindowsTerminalPreferences.cs
@@ -96,7 +96,7 @@ protected override void setFactories()
// This is a transient dependency coming from Cyberduck.Cryptomator through Cyberduck.Cli,
// which isn't used in duck. Thus crazy stuff happens, and we have to force-load Cyberduck.Cryptomator here.
// ref https://github.com/iterate-ch/cyberduck/issues/12812
- this.setDefault("factory.vault.class", typeof(CryptoVault).AssemblyQualifiedName);
+ this.setDefault("factory.vaultprovider.class", typeof(CryptoVaultProvider).AssemblyQualifiedName);
}
private class TerminalPropertyStoreFactory : IPropertyStoreFactory
diff --git a/cli/src/main/java/ch/cyberduck/cli/Terminal.java b/cli/src/main/java/ch/cyberduck/cli/Terminal.java
index c7f002840c8..486be082873 100644
--- a/cli/src/main/java/ch/cyberduck/cli/Terminal.java
+++ b/cli/src/main/java/ch/cyberduck/cli/Terminal.java
@@ -48,6 +48,7 @@
import ch.cyberduck.core.transfer.TransferPrompt;
import ch.cyberduck.core.transfer.TransferSpeedometer;
import ch.cyberduck.core.vault.LoadingVaultLookupListener;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.vault.VaultRegistry;
import ch.cyberduck.core.vault.VaultRegistryFactory;
import ch.cyberduck.core.worker.AttributesWorker;
@@ -247,7 +248,7 @@ public void uncaughtException(final Thread t, final Throwable e) {
log.debug("Attempting to load vault from {}", vault);
try {
this.execute(new TerminalBackgroundAction<>(controller, source, new LoadVaultWorker(new LoadingVaultLookupListener(source.getVaultRegistry(),
- new TerminalPasswordCallback()), vault)));
+ new TerminalPasswordCallback()), new VaultMetadata(vault, VaultMetadata.Type.valueOf(preferences.getProperty("cryptomator.vault.default"))))));
}
catch(TerminalBackgroundException e) {
return Exit.failure;
diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java b/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java
index dd13d2bfc4f..a6321cedc09 100644
--- a/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java
+++ b/cli/src/main/java/ch/cyberduck/cli/TerminalPreferences.java
@@ -17,7 +17,8 @@
import ch.cyberduck.core.DisabledConnectionTimeout;
import ch.cyberduck.core.Local;
import ch.cyberduck.core.Permission;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.CryptoVaultProvider;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
import ch.cyberduck.core.preferences.Preferences;
import ch.cyberduck.core.transfer.Transfer;
@@ -58,7 +59,7 @@ protected void setFactories() {
for(Transfer.Type t : Transfer.Type.values()) {
this.setDefault(String.format("factory.transferpromptcallback.%s.class", t.name()), TerminalTransferPrompt.class.getName());
}
- this.setDefault("factory.vault.class", CryptoVault.class.getName());
+ this.setDefault("factory.vaultprovider.class", CryptoVaultProvider.class.getName());
this.setDefault("factory.securerandom.class", FastSecureRandomProvider.class.getName());
this.setDefault("factory.connectiontimeout.class", DisabledConnectionTimeout.class.getName());
}
diff --git a/cli/windows/pom.xml b/cli/windows/pom.xml
index b7ef39c6e37..9ab75e81c5d 100644
--- a/cli/windows/pom.xml
+++ b/cli/windows/pom.xml
@@ -20,7 +20,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
cli-windows
Cyberduck CLI Windows
diff --git a/core/dll/pom.xml b/core/dll/pom.xml
index 250ef599fbd..4a01caead0d 100644
--- a/core/dll/pom.xml
+++ b/core/dll/pom.xml
@@ -5,7 +5,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
Cyberduck.Core
pom
diff --git a/core/dylib/pom.xml b/core/dylib/pom.xml
index dfdbb04f2da..398eee705a8 100644
--- a/core/dylib/pom.xml
+++ b/core/dylib/pom.xml
@@ -5,7 +5,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
libcore
diff --git a/core/native/pom.xml b/core/native/pom.xml
index 0a632569837..f36b59cefeb 100644
--- a/core/native/pom.xml
+++ b/core/native/pom.xml
@@ -5,7 +5,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
Cyberduck.Core.Native
pom
diff --git a/core/native/refresh/pom.xml b/core/native/refresh/pom.xml
index 3835095fd9e..8d0474d5892 100644
--- a/core/native/refresh/pom.xml
+++ b/core/native/refresh/pom.xml
@@ -5,7 +5,7 @@
ch.cyberduck
Cyberduck.Core.Native
../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
Cyberduck.Core.Refresh
pom
diff --git a/core/pom.xml b/core/pom.xml
index f4734797079..4e62f705f2e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -18,11 +18,15 @@
ch.cyberduck
parent
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
core
jar
+
+ 10.5
+
+
@@ -144,6 +148,11 @@
auto-service-annotations
1.1.1
+
+ com.nimbusds
+ nimbus-jose-jwt
+ ${nimbus-jose.version}
+
junit
junit
diff --git a/core/src/main/java/ch/cyberduck/core/ExpiringObjectHolder.java b/core/src/main/java/ch/cyberduck/core/ExpiringObjectHolder.java
index 1a2b1aa52e3..6a7634caf49 100644
--- a/core/src/main/java/ch/cyberduck/core/ExpiringObjectHolder.java
+++ b/core/src/main/java/ch/cyberduck/core/ExpiringObjectHolder.java
@@ -40,6 +40,15 @@ public T get() {
return object;
}
log.warn("Expired object {}", object);
+ return this.expire();
+ }
+
+ public T expire() {
+ this.destroy(object);
return object = null;
}
+
+ protected void destroy(final Object object) {
+ //
+ }
}
diff --git a/core/src/main/java/ch/cyberduck/core/PathAttributes.java b/core/src/main/java/ch/cyberduck/core/PathAttributes.java
index 69ae359ef2b..78811ae4a0d 100644
--- a/core/src/main/java/ch/cyberduck/core/PathAttributes.java
+++ b/core/src/main/java/ch/cyberduck/core/PathAttributes.java
@@ -23,6 +23,7 @@
import ch.cyberduck.core.io.Checksum;
import ch.cyberduck.core.serializer.Serializer;
import ch.cyberduck.core.transfer.TransferStatus;
+import ch.cyberduck.core.vault.VaultMetadata;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@@ -145,6 +146,11 @@ public class PathAttributes extends Attributes implements Serializable {
* Cryptomator vault
*/
private Path vault;
+
+ /**
+ * Cryptomator vault metadata
+ */
+ private VaultMetadata vaultMetadata;
/**
* Cryptomator decrypted path
*/
@@ -156,7 +162,7 @@ public class PathAttributes extends Attributes implements Serializable {
/**
* Unique identifier for cryptomator
*/
- private String directoryId;
+ private byte[] directoryId;
private Map custom = Collections.emptyMap();
@@ -199,6 +205,7 @@ public PathAttributes(final PathAttributes copy) {
custom = new HashMap<>(copy.custom);
verdict = copy.verdict;
vault = copy.vault;
+ vaultMetadata = copy.vaultMetadata;
decrypted = copy.decrypted;
encrypted = copy.encrypted;
directoryId = copy.directoryId;
@@ -287,6 +294,16 @@ public T serialize(final Serializer dict) {
dict.setObjectForKey(vault, "Vault");
}
}
+ if(vaultMetadata != null) {
+ if(vaultMetadata.root != null) {
+ if(vaultMetadata.root.attributes() == this) {
+ log.debug("Skip serializing vault metadata root attribute {} to avoid recursion", vaultMetadata.root);
+ }
+ else {
+ dict.setObjectForKey(vaultMetadata, "Vault Metadata");
+ }
+ }
+ }
if(!custom.isEmpty()) {
dict.setMapForKey(custom, "Custom");
}
@@ -480,11 +497,11 @@ public PathAttributes setLockId(final String lockId) {
return this;
}
- public String getDirectoryId() {
+ public byte[] getDirectoryId() {
return directoryId;
}
- public PathAttributes setDirectoryId(final String directoryId) {
+ public PathAttributes setDirectoryId(final byte[] directoryId) {
this.directoryId = directoryId;
return this;
}
@@ -534,6 +551,15 @@ public Path getVault() {
return vault;
}
+ public PathAttributes setVaultMetadata(final VaultMetadata vaultMetadata) {
+ this.vaultMetadata = vaultMetadata;
+ return this;
+ }
+
+ public VaultMetadata getVaultMetadata() {
+ return vaultMetadata;
+ }
+
/**
* If the path should not be displayed in a browser by default unless the user explicitly chooses to show hidden
* files.
@@ -803,7 +829,7 @@ public PathAttributes setLockId(final String lockId) {
}
@Override
- public PathAttributes setDirectoryId(final String directoryId) {
+ public PathAttributes setDirectoryId(final byte[] directoryId) {
return this;
}
diff --git a/core/src/main/java/ch/cyberduck/core/features/Vault.java b/core/src/main/java/ch/cyberduck/core/features/Vault.java
index 23243500404..1ad7a427340 100644
--- a/core/src/main/java/ch/cyberduck/core/features/Vault.java
+++ b/core/src/main/java/ch/cyberduck/core/features/Vault.java
@@ -23,7 +23,8 @@
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.exception.UnsupportedException;
import ch.cyberduck.core.vault.DisabledVault;
-import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultMetadataProvider;
public interface Vault {
@@ -35,7 +36,8 @@ public interface Vault {
* @throws BackgroundException Failure reading master key from server
* @throws NotfoundException No master key file in home
*/
- Path create(Session> session, String region, VaultCredentials credentials) throws BackgroundException;
+
+ Vault create(Session> session, String region, VaultMetadataProvider metadata) throws BackgroundException;
/**
* Open existing vault
@@ -45,7 +47,7 @@ public interface Vault {
* @throws BackgroundException Failure reading master key from server
* @throws NotfoundException No master key file in home
*/
- Vault load(Session> session, PasswordCallback prompt) throws BackgroundException;
+ Vault load(Session> session, PasswordCallback prompt, VaultMetadataProvider provider) throws BackgroundException;
/**
* Close vault
@@ -102,6 +104,8 @@ public interface Vault {
*/
Path getHome();
+ VaultMetadata getMetadata();
+
enum State {
open,
closed
diff --git a/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java b/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java
index c1d686e9701..5fd9e39c51e 100755
--- a/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java
+++ b/core/src/main/java/ch/cyberduck/core/preferences/Preferences.java
@@ -62,6 +62,7 @@
import ch.cyberduck.core.urlhandler.DisabledSchemeHandler;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.DisabledVault;
+import ch.cyberduck.core.vault.DisabledVaultProvider;
import ch.cyberduck.core.webloc.InternetShortcutFileWriter;
import ch.cyberduck.ui.quicklook.ApplicationLauncherQuicklook;
@@ -505,6 +506,7 @@ protected void setFactories() {
this.setDefault("factory.threadpool.class", DefaultThreadPool.class.getName());
this.setDefault("factory.urlfilewriter.class", InternetShortcutFileWriter.class.getName());
this.setDefault("factory.vault.class", DisabledVault.class.getName());
+ this.setDefault("factory.vaultprovider.class", DisabledVaultProvider.class.getName());
this.setDefault("factory.vaultregistry.class", DefaultVaultRegistry.class.getName());
this.setDefault("factory.securerandom.class", DefaultSecureRandomProvider.class.getName());
this.setDefault("factory.providerhelpservice.class", DefaultProviderHelpService.class.getName());
diff --git a/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java b/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java
index 0ad7f805a2b..a54bfed364f 100644
--- a/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java
+++ b/core/src/main/java/ch/cyberduck/core/serializer/PathAttributesDictionary.java
@@ -24,6 +24,7 @@
import ch.cyberduck.core.features.Quota;
import ch.cyberduck.core.io.Checksum;
import ch.cyberduck.core.io.HashAlgorithm;
+import ch.cyberduck.core.vault.VaultMetadataDictionary;
import java.util.Collections;
import java.util.Map;
@@ -117,6 +118,10 @@ public PathAttributes deserialize(final T serialized) {
if(vaultObj != null) {
attributes.setVault(new PathDictionary<>(factory).deserialize(vaultObj));
}
+ final T vaultMetadataObj = dict.objectForKey("Vault Metadata");
+ if(vaultMetadataObj != null) {
+ attributes.setVaultMetadata(new VaultMetadataDictionary<>(factory).deserialize(vaultMetadataObj));
+ }
final Map customObj = dict.mapForKey("Custom");
if(customObj != null) {
attributes.setCustom(customObj);
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java b/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java
index 9550e607bbe..6787da34a05 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/DefaultVaultRegistry.java
@@ -23,14 +23,12 @@
import ch.cyberduck.core.SimplePathPredicate;
import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.features.*;
-import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.vault.registry.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -41,9 +39,14 @@ public class DefaultVaultRegistry extends CopyOnWriteArraySet implements
PreferencesFactory.get().getProperty("cryptomator.vault.masterkey.filename");
public static final String DEFAULT_BACKUPKEY_FILE_NAME = String.format("%s.bkup",
PreferencesFactory.get().getProperty("cryptomator.vault.masterkey.filename"));
+
+ // TODO können die weg? wird z.b. weiter unten via hostpreferences geholt
public static final String DEFAULT_VAULTCONFIG_FILE_NAME =
PreferencesFactory.get().getProperty("cryptomator.vault.config.filename");
+ public static final String DEFAULT_VAULTCONFIGUVF_FILE_NAME =
+ PreferencesFactory.get().getProperty("cryptomator.vault.config.filename.uvf");
+
private final PasswordCallback prompt;
public DefaultVaultRegistry(final PasswordCallback prompt) {
@@ -73,7 +76,7 @@ public boolean close(final Path directory) {
});
}
finally {
- directory.attributes().setVault(null);
+ directory.attributes().setVaultMetadata(null);
}
}
@@ -107,18 +110,14 @@ public Vault find(final Session> session, final Path file, final boolean autol
}
if(autoload) {
final LoadingVaultLookupListener listener = new LoadingVaultLookupListener(this, prompt);
- if(file.attributes().getVault() != null) {
- return listener.load(session, file.attributes().getVault(),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8));
+ if(file.attributes().getVaultMetadata() != null) {
+ return listener.load(session, file.attributes().getVaultMetadata()
+ );
}
final Path directory = file.getParent();
- if(directory.attributes().getVault() != null) {
- return listener.load(session, directory.attributes().getVault(),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8));
+ if(directory.attributes().getVaultMetadata() != null) {
+ return listener.load(session, directory.attributes().getVaultMetadata()
+ );
}
}
return Vault.DISABLED;
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java b/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java
index 70d119aede1..f252220feff 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/DisabledVault.java
@@ -18,6 +18,7 @@
import ch.cyberduck.core.PasswordCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
+import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Vault;
import java.util.EnumSet;
@@ -36,12 +37,12 @@ public DisabledVault(final Path home) {
}
@Override
- public Path create(final Session> session, final String region, final VaultCredentials credentials) {
+ public Vault create(final Session> session, final String region, final VaultMetadataProvider metadata) throws BackgroundException {
return null;
}
@Override
- public Vault load(final Session> session, final PasswordCallback prompt) {
+ public Vault load(final Session> session, final PasswordCallback prompt, final VaultMetadataProvider provider) {
return this;
}
@@ -96,6 +97,10 @@ public Path getHome() {
return home;
}
+ @Override
+ public VaultMetadata getMetadata() {
+ return null;
+ }
@Override
public boolean equals(final Object o) {
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java
index f1cd16e3f11..2eff02bead2 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultLookupListener.java
@@ -15,7 +15,6 @@
* GNU General Public License for more details.
*/
-import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.features.Vault;
@@ -26,8 +25,8 @@ public final class DisabledVaultLookupListener implements VaultLookupListener {
private static final Logger log = LogManager.getLogger(DisabledVaultLookupListener.class);
@Override
- public Vault load(final Session session, final Path directory, final String masterkey, final String config, final byte[] pepper) {
- log.warn("Ignore vault {}", directory);
+ public Vault load(final Session session, final VaultMetadata metadata) {
+ log.warn("Ignore vault {}", metadata);
return Vault.DISABLED;
}
}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultProvider.java b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultProvider.java
new file mode 100644
index 00000000000..e096580ac26
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/DisabledVaultProvider.java
@@ -0,0 +1,50 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListProgressListener;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.Find;
+import ch.cyberduck.core.features.Vault;
+
+public class DisabledVaultProvider implements VaultProvider {
+ @Override
+ public boolean isVault(final Path path) {
+ return false;
+ }
+
+ @Override
+ public VaultMetadata metadata(final Path path) {
+ return null;
+ }
+
+ @Override
+ public VaultMetadata find(final Path directory, final Find find, final ListProgressListener listener) throws BackgroundException {
+ return null;
+ }
+
+ @Override
+ public Vault provide(final Session> session, final VaultMetadata metadata) {
+ return Vault.DISABLED;
+ }
+
+ @Override
+ public Vault create(final Session> session, final String region, final VaultCredentials credentials, final VaultMetadata metadata) throws BackgroundException {
+ return Vault.DISABLED;
+ }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/JWKCallback.java b/core/src/main/java/ch/cyberduck/core/vault/JWKCallback.java
new file mode 100644
index 00000000000..ce5b2e06926
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/JWKCallback.java
@@ -0,0 +1,36 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Host;
+import ch.cyberduck.core.LoginOptions;
+import ch.cyberduck.core.PasswordCallback;
+import ch.cyberduck.core.exception.LoginCanceledException;
+
+public interface JWKCallback extends PasswordCallback {
+
+ @Override
+ JWKCredentials prompt(Host bookmark, String title, String reason, LoginOptions options) throws LoginCanceledException;
+
+ static JWKCallback cast(PasswordCallback callback) {
+ if(callback instanceof JWKCallback) {
+ return (JWKCallback) callback;
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported metadata type " + callback.getClass());
+ }
+ }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/JWKCredentials.java b/core/src/main/java/ch/cyberduck/core/vault/JWKCredentials.java
new file mode 100644
index 00000000000..b972cd7116c
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/JWKCredentials.java
@@ -0,0 +1,32 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import com.nimbusds.jose.jwk.JWK;
+
+public class JWKCredentials extends VaultCredentials {
+
+ private final JWK key;
+
+ public JWKCredentials(final JWK key) {
+ super(null);
+ this.key = key;
+ }
+
+ public JWK getKey() {
+ return key;
+ }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java b/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java
index 242f903b8d5..3d9299f2cc6 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/LoadingVaultLookupListener.java
@@ -16,8 +16,6 @@
*/
import ch.cyberduck.core.PasswordCallback;
-import ch.cyberduck.core.PasswordStore;
-import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Vault;
@@ -37,21 +35,23 @@ public LoadingVaultLookupListener(final VaultRegistry registry, final PasswordCa
}
@Override
- public Vault load(final Session session, final Path directory, final String masterkey, final String config, final byte[] pepper) throws VaultUnlockCancelException {
+ public Vault load(final Session session, final VaultMetadata metadata) throws VaultUnlockCancelException {
synchronized(registry) {
- if(registry.contains(directory)) {
- return registry.find(session, directory);
+ if(registry.contains(metadata.root)) {
+ return registry.find(session, metadata.root);
}
- final Vault vault = VaultFactory.get(directory, masterkey, config, pepper);
- log.info("Loading vault {} for session {}", vault, session);
+ log.info("Loading vault for session {}", session);
+ final Vault vault = VaultProviderFactory.get(session).provide(session, metadata);
try {
- registry.add(vault.load(session, prompt));
+ // TODO provide correct metadata provider
+ registry.add(vault.load(session, prompt, new VaultMetadataProvider() {
+ }));
+ return vault;
}
catch(BackgroundException e) {
- log.warn("Failure {} loading vault {}", e, vault);
+ log.warn("Failure {} loading vault", e);
throw new VaultUnlockCancelException(vault, e);
}
- return vault;
}
}
}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java b/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java
index 4f9eb821ce2..4c0249678e9 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultFinderListProgressListener.java
@@ -23,12 +23,10 @@
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.features.Vault;
-import ch.cyberduck.core.preferences.HostPreferencesFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -38,21 +36,17 @@ public class VaultFinderListProgressListener extends IndexedListProgressListener
private final Session> session;
private final VaultLookupListener lookup;
private final ListProgressListener proxy;
- private final String config;
- private final String masterkey;
- private final byte[] pepper;
// Number of files to wait for until proxy is notified of files
private final int filecount;
private final AtomicBoolean canceled = new AtomicBoolean();
+ private final VaultProvider provider;
public VaultFinderListProgressListener(final Session> session, final VaultLookupListener lookup, final ListProgressListener proxy, final int filecount) {
this.session = session;
this.lookup = lookup;
this.proxy = proxy;
- this.config = HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename");
- this.masterkey = HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename");
- this.pepper = HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8);
this.filecount = filecount;
+ this.provider = VaultProviderFactory.get(session);
}
@Override
@@ -83,10 +77,10 @@ public void chunk(final Path folder, final AttributedList list) throws Con
@Override
public void visit(final AttributedList list, final int index, final Path file) throws ConnectionCanceledException {
- final Path directory = file.getParent();
- if(config.equals(file.getName()) || masterkey.equals(file.getName())) {
+ final VaultMetadata metadata = provider.metadata(file);
+ if(metadata != null) {
log.info("Found vault config or masterkey file {}", file);
- final Vault vault = lookup.load(session, directory, masterkey, config, pepper);
+ final Vault vault = lookup.load(session, metadata);
if(vault.equals(Vault.DISABLED)) {
return;
}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java b/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java
index 7e4f12cbbb2..56782de4096 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultLookupListener.java
@@ -15,10 +15,9 @@
* GNU General Public License for more details.
*/
-import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.features.Vault;
public interface VaultLookupListener {
- Vault load(final Session session, Path directory, String masterkey, final String config, byte[] pepper) throws VaultUnlockCancelException;
+ Vault load(final Session session, VaultMetadata metadata) throws VaultUnlockCancelException;
}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultMetadata.java b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadata.java
new file mode 100644
index 00000000000..468b609d344
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadata.java
@@ -0,0 +1,87 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Serializable;
+import ch.cyberduck.core.serializer.Serializer;
+
+import java.util.Objects;
+
+public class VaultMetadata implements Serializable {
+
+ public Path root;
+ public Type type;
+
+ public enum Type {
+ V8, UVF
+ }
+
+ public VaultMetadata() {
+ }
+
+ public VaultMetadata(final Path path, final Type type) {
+ this.root = path;
+ this.type = type;
+ }
+
+ @Override
+ public T serialize(final Serializer dict) {
+ if(root != null) {
+ dict.setObjectForKey(root, "Root");
+ }
+ if(type != null) {
+ dict.setStringForKey(type.name(), "Type");
+ }
+ return dict.getSerialized();
+ }
+
+ @Override
+ public final boolean equals(final Object o) {
+ if(o == this) {
+ return true;
+ }
+ if(!(o instanceof VaultMetadata)) {
+ return false;
+ }
+
+ VaultMetadata that = (VaultMetadata) o;
+ if(!Objects.equals(root, that.root)) {
+ return false;
+ }
+ if(type != that.type) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(root);
+ result = 31 * result + Objects.hashCode(type);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("VaultMetadata{");
+ sb.append("root=").append(root);
+ sb.append(", type=").append(type);
+ sb.append('}');
+ return sb.toString();
+ }
+}
+
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataDictionary.java b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataDictionary.java
new file mode 100644
index 00000000000..e3bd813a80e
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataDictionary.java
@@ -0,0 +1,47 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.DeserializerFactory;
+import ch.cyberduck.core.serializer.Deserializer;
+import ch.cyberduck.core.serializer.PathDictionary;
+
+public class VaultMetadataDictionary {
+
+ private final DeserializerFactory factory;
+
+ public VaultMetadataDictionary() {
+ this.factory = new DeserializerFactory<>();
+ }
+
+ public VaultMetadataDictionary(final DeserializerFactory factory) {
+ this.factory = factory;
+ }
+
+ public VaultMetadata deserialize(final T serialized) {
+ final Deserializer dict = factory.create(serialized);
+ final VaultMetadata vaultMetadata = new VaultMetadata();
+ final T vaultObj = dict.objectForKey("Root");
+ if(vaultObj != null) {
+ vaultMetadata.root = new PathDictionary<>(factory).deserialize(vaultObj);
+ }
+ final String type = dict.stringForKey("Type");
+ if(type != null) {
+ vaultMetadata.type = VaultMetadata.Type.valueOf(type);
+ }
+ return vaultMetadata;
+ }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataProvider.java b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataProvider.java
new file mode 100644
index 00000000000..095e0376e08
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultMetadataProvider.java
@@ -0,0 +1,20 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+public interface VaultMetadataProvider {
+ // Marker interface
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultProvider.java b/core/src/main/java/ch/cyberduck/core/vault/VaultProvider.java
new file mode 100644
index 00000000000..7415052e739
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultProvider.java
@@ -0,0 +1,37 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListProgressListener;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.Find;
+import ch.cyberduck.core.features.Vault;
+
+public interface VaultProvider {
+ boolean isVault(Path path);
+
+ VaultMetadata metadata(Path path);
+
+ VaultMetadata find(Path directory, Find find, ListProgressListener listener) throws BackgroundException;
+
+ Vault provide(Session> session, VaultMetadata metadata);
+
+ Vault create(Session> session, String region, VaultCredentials credentials, VaultMetadata metadata) throws BackgroundException;
+
+ VaultProvider DISABLED = new DisabledVaultProvider();
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/VaultProviderFactory.java b/core/src/main/java/ch/cyberduck/core/vault/VaultProviderFactory.java
new file mode 100644
index 00000000000..172fc59d255
--- /dev/null
+++ b/core/src/main/java/ch/cyberduck/core/vault/VaultProviderFactory.java
@@ -0,0 +1,55 @@
+package ch.cyberduck.core.vault;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Factory;
+import ch.cyberduck.core.Session;
+
+import org.apache.commons.lang3.reflect.ConstructorUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+public class VaultProviderFactory extends Factory {
+ private static final Logger log = LogManager.getLogger(VaultProviderFactory.class);
+
+ private VaultProviderFactory() {
+ super("factory.vaultprovider.class");
+ }
+
+ public static VaultProvider get(final Session> session) {
+ return new VaultProviderFactory().create(session);
+ }
+
+ private VaultProvider create(final Session> session) {
+ try {
+ final Constructor extends VaultProvider> constructor = ConstructorUtils.getMatchingAccessibleConstructor(clazz,
+ session.getClass());
+ if(null == constructor) {
+ log.warn("No matching constructor for parameter {}", session.getClass());
+ // Call default constructor for disabled implementations
+ return clazz.getDeclaredConstructor().newInstance();
+ }
+ return constructor.newInstance(session);
+ }
+ catch(InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
+ log.error("Failure loading callback class {}. {}", clazz, e.getMessage());
+ return VaultProvider.DISABLED;
+ }
+ }
+}
diff --git a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java
index 90570bc7c35..c05b9d2a0c5 100644
--- a/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java
+++ b/core/src/main/java/ch/cyberduck/core/vault/registry/VaultRegistryFindFeature.java
@@ -23,13 +23,15 @@
import ch.cyberduck.core.features.Vault;
import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.vault.VaultLookupListener;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultProvider;
+import ch.cyberduck.core.vault.VaultProviderFactory;
import ch.cyberduck.core.vault.VaultRegistry;
import ch.cyberduck.core.vault.VaultUnlockCancelException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
public class VaultRegistryFindFeature implements Find {
@@ -40,6 +42,7 @@ public class VaultRegistryFindFeature implements Find {
private final VaultRegistry registry;
private final VaultLookupListener lookup;
private final boolean autodetect;
+ private final VaultProvider provider;
public VaultRegistryFindFeature(final Session> session, final Find proxy, final VaultRegistry registry, final VaultLookupListener lookup) {
this.session = session;
@@ -48,6 +51,7 @@ public VaultRegistryFindFeature(final Session> session, final Find proxy, fina
this.lookup = lookup;
this.autodetect = HostPreferencesFactory.get(session.getHost()).getBoolean("cryptomator.vault.autodetect")
&& HostPreferencesFactory.get(session.getHost()).getBoolean("cryptomator.enable");
+ this.provider = VaultProviderFactory.get(session);
}
@Override
@@ -60,14 +64,13 @@ public boolean find(final Path file, final ListProgressListener listener) throws
HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"), EnumSet.of(Path.Type.file));
final Path key = new Path(directory,
HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"), EnumSet.of(Path.Type.file));
- if(proxy.find(vaultConfig, listener) || proxy.find(key, listener)) {
+
+ final VaultMetadata metadata = provider.find(directory, proxy, listener);
+ if(metadata != null) {
log.info("Found vault config {} or masterkey {}", vaultConfig, key);
try {
log.info("Found vault {}", directory);
- return lookup.load(session, directory,
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8))
+ return lookup.load(session, metadata)
.getFeature(session, Find.class, proxy)
.find(file, listener);
}
diff --git a/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java b/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java
index 85d274acccf..7dedd68669b 100644
--- a/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java
+++ b/core/src/main/java/ch/cyberduck/core/worker/CreateVaultWorker.java
@@ -16,38 +16,38 @@
*/
import ch.cyberduck.core.LocaleFactory;
-import ch.cyberduck.core.PasswordStore;
-import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Vault;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultProviderFactory;
import java.text.MessageFormat;
import java.util.Objects;
-public class CreateVaultWorker extends Worker {
+public class CreateVaultWorker extends Worker {
private final String region;
private final VaultCredentials passphrase;
- private final Vault vault;
+ private final VaultMetadata metadata;
- public CreateVaultWorker(final String region, final VaultCredentials passphrase, final Vault vault) {
+ public CreateVaultWorker(final String region, final VaultCredentials passphrase, final VaultMetadata metadata) {
this.region = region;
this.passphrase = passphrase;
- this.vault = vault;
+ this.metadata = metadata;
}
@Override
- public Path run(final Session> session) throws BackgroundException {
- final Path home = vault.create(session, region, passphrase);
+ public Vault run(final Session> session) throws BackgroundException {
+ final Vault vault = VaultProviderFactory.get(session).create(session, region, passphrase, metadata);
vault.close();
- return home;
+ return vault;
}
@Override
public String getActivity() {
- return MessageFormat.format(LocaleFactory.localizedString("Making directory {0}", "Status"), vault.getHome().getName());
+ return MessageFormat.format(LocaleFactory.localizedString("Making directory {0}", "Status"), metadata.root.getName());
}
@Override
@@ -59,18 +59,18 @@ public boolean equals(final Object o) {
return false;
}
final CreateVaultWorker that = (CreateVaultWorker) o;
- return Objects.equals(vault, that.vault);
+ return Objects.equals(metadata, that.metadata);
}
@Override
public int hashCode() {
- return Objects.hash(vault);
+ return Objects.hash(metadata);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("CreateVaultWorker{");
- sb.append("vault=").append(vault);
+ sb.append("metadata=").append(metadata);
sb.append('}');
return sb.toString();
}
diff --git a/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java b/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java
index 334d3b2c189..1b56778c45e 100644
--- a/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java
+++ b/core/src/main/java/ch/cyberduck/core/worker/LoadVaultWorker.java
@@ -16,32 +16,27 @@
*/
import ch.cyberduck.core.LocaleFactory;
-import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Vault;
-import ch.cyberduck.core.preferences.HostPreferencesFactory;
import ch.cyberduck.core.vault.VaultLookupListener;
+import ch.cyberduck.core.vault.VaultMetadata;
-import java.nio.charset.StandardCharsets;
import java.util.Objects;
public class LoadVaultWorker extends Worker {
private final VaultLookupListener listener;
- private final Path directory;
+ private final VaultMetadata metadata;
- public LoadVaultWorker(final VaultLookupListener listener, final Path directory) {
+ public LoadVaultWorker(final VaultLookupListener listener, final VaultMetadata metadata) {
this.listener = listener;
- this.directory = directory;
+ this.metadata = metadata;
}
@Override
public Vault run(final Session> session) throws BackgroundException {
- return listener.load(session, directory,
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename"),
- HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8));
+ return listener.load(session, metadata);
}
@Override
@@ -58,18 +53,18 @@ public boolean equals(final Object o) {
return false;
}
final LoadVaultWorker that = (LoadVaultWorker) o;
- return Objects.equals(directory, that.directory);
+ return Objects.equals(metadata, that.metadata);
}
@Override
public int hashCode() {
- return Objects.hash(directory);
+ return Objects.hash(metadata);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("LoadVaultWorker{");
- sb.append("directory=").append(directory);
+ sb.append("metadata=").append(metadata);
sb.append('}');
return sb.toString();
}
diff --git a/cryptomator/dll/pom.xml b/cryptomator/dll/pom.xml
index da67b9b2759..310f49d9d1a 100644
--- a/cryptomator/dll/pom.xml
+++ b/cryptomator/dll/pom.xml
@@ -20,7 +20,7 @@
ch.cyberduck
parent
../../pom.xml
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
Cyberduck.Cryptomator
pom
diff --git a/cryptomator/pom.xml b/cryptomator/pom.xml
index 22b797aa85e..ec66f62f008 100644
--- a/cryptomator/pom.xml
+++ b/cryptomator/pom.xml
@@ -19,13 +19,13 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
cryptomator
jar
- 2.1.2.1
+ 2.3.0.uvfdraft-SNAPSHOT
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java
new file mode 100644
index 00000000000..a5334503d3a
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java
@@ -0,0 +1,422 @@
+package ch.cyberduck.core.cryptomator;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListService;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.PathAttributes;
+import ch.cyberduck.core.Permission;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.UrlProvider;
+import ch.cyberduck.core.cryptomator.features.*;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.UnsupportedException;
+import ch.cyberduck.core.features.*;
+import ch.cyberduck.core.shared.DefaultTouchFeature;
+import ch.cyberduck.core.transfer.TransferStatus;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.AuthenticationFailedException;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.DirectoryContentCryptor;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+import org.cryptomator.cryptolib.api.FileContentCryptor;
+import org.cryptomator.cryptolib.api.FileHeaderCryptor;
+import org.cryptomator.cryptolib.api.Masterkey;
+
+import java.util.EnumSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class AbstractVault implements Vault {
+
+ private static final Logger log = LogManager.getLogger(AbstractVault.class);
+
+ public abstract Path getMasterkeyPath();
+
+ public abstract Masterkey getMasterkey();
+
+ public abstract Path getConfig();
+
+ public abstract FileHeaderCryptor getFileHeaderCryptor();
+
+ public abstract FileContentCryptor getFileContentCryptor();
+
+ public abstract CryptoFilename getFilenameProvider();
+
+ public abstract CryptoDirectory getDirectoryProvider();
+
+ public abstract Cryptor getCryptor();
+
+ public abstract int getNonceSize();
+
+ public abstract Pattern getFilenamePattern();
+
+ public int numberOfChunks(long cleartextFileSize) {
+ return (int) (cleartextFileSize / this.getFileContentCryptor().cleartextChunkSize() +
+ ((cleartextFileSize % this.getFileContentCryptor().cleartextChunkSize() > 0) ? 1 : 0));
+ }
+
+ public long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws CryptoInvalidFilesizeException {
+ if(TransferStatus.UNKNOWN_LENGTH == ciphertextFileSize) {
+ return TransferStatus.UNKNOWN_LENGTH;
+ }
+ final int headerSize;
+ if(0L == cleartextFileOffset) {
+ headerSize = this.getFileHeaderCryptor().headerSize();
+ }
+ else {
+ headerSize = 0;
+ }
+ try {
+ return this.getFileContentCryptor().cleartextSize(ciphertextFileSize - headerSize);
+ }
+ catch(AssertionError e) {
+ throw new CryptoInvalidFilesizeException(String.format("Encrypted file size must be at least %d bytes", headerSize));
+ }
+ catch(IllegalArgumentException e) {
+ throw new CryptoInvalidFilesizeException(String.format("Invalid file size. %s", e.getMessage()));
+ }
+ }
+
+ @Override
+ public State getState() {
+ return this.isUnlocked() ? State.open : State.closed;
+ }
+
+ @Override
+ public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) {
+ if(TransferStatus.UNKNOWN_LENGTH == cleartextFileSize) {
+ return TransferStatus.UNKNOWN_LENGTH;
+ }
+ final int headerSize;
+ if(0L == cleartextFileOffset) {
+ headerSize = this.getCryptor().fileHeaderCryptor().headerSize();
+ }
+ else {
+ headerSize = 0;
+ }
+ return headerSize + this.getCryptor().fileContentCryptor().ciphertextSize(cleartextFileSize);
+ }
+
+ @Override
+ public Path encrypt(Session> session, Path file) throws BackgroundException {
+ return this.encrypt(session, file, false);
+ }
+
+ @Override
+ public Path encrypt(Session> session, Path file, boolean metadata) throws BackgroundException {
+ final Path encrypted;
+ if(file.isFile() || metadata) {
+ if(file.getType().contains(Path.Type.vault)) {
+ log.warn("Skip file {} because it is marked as an internal vault path", file);
+ return file;
+ }
+ if(new SimplePathPredicate(file).test(this.getHome())) {
+ log.warn("Skip vault home {} because the root has no metadata file", file);
+ return file;
+ }
+ final Path parent;
+ final String filename;
+ if(file.getType().contains(Path.Type.encrypted)) {
+ final Path decrypted = file.attributes().getDecrypted();
+ parent = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent());
+ filename = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent(), decrypted.getName(), decrypted.getType());
+ }
+ else {
+ parent = this.getDirectoryProvider().toEncrypted(session, file.getParent());
+ filename = this.getDirectoryProvider().toEncrypted(session, file.getParent(), file.getName(), file.getType());
+ }
+ final PathAttributes attributes = new PathAttributes(file.attributes());
+ attributes.setDirectoryId(null);
+ if(!file.isFile() && !metadata) {
+ // The directory is different from the metadata file used to resolve the actual folder
+ attributes.setVersionId(null);
+ attributes.setFileId(null);
+ }
+ // Translate file size
+ attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize()));
+ final EnumSet type = EnumSet.copyOf(file.getType());
+ type.remove(Path.Type.decrypted);
+ type.add(Path.Type.encrypted);
+ encrypted = new Path(parent, filename, type, attributes);
+ }
+ else {
+ if(file.getType().contains(Path.Type.encrypted)) {
+ log.warn("Skip file {} because it is already marked as an encrypted path", file);
+ return file;
+ }
+ if(file.getType().contains(Path.Type.vault)) {
+ return this.getDirectoryProvider().toEncrypted(session, this.getHome());
+ }
+ encrypted = this.getDirectoryProvider().toEncrypted(session, file);
+ }
+ // Add reference to decrypted file
+ if(!file.getType().contains(Path.Type.encrypted)) {
+ encrypted.attributes().setDecrypted(file);
+ }
+ // Add reference for vault
+ file.attributes().setVaultMetadata(this.getMetadata());
+ encrypted.attributes().setVaultMetadata(this.getMetadata());
+ return encrypted;
+ }
+
+ @Override
+ public Path decrypt(final Session> session, final Path file) throws BackgroundException {
+ if(file.getType().contains(Path.Type.decrypted)) {
+ log.warn("Skip file {} because it is already marked as a decrypted path", file);
+ return file;
+ }
+ if(file.getType().contains(Path.Type.vault)) {
+ log.warn("Skip file {} because it is marked as an internal vault path", file);
+ return file;
+ }
+ final Path inflated = this.inflate(session, file);
+ final Pattern pattern = this.getFilenamePattern();
+ final Matcher m = pattern.matcher(inflated.getName());
+ if(m.matches()) {
+ try {
+ //TODO lädt das recovery metadaten file anstatt normales
+ final DirectoryContentCryptor.Decrypting decrypting = this.getFilenameDecryptor(session, file);
+ //TODO hier hatten wir caching via CryptorCache
+ final String cleartextFilename = decrypting.decrypt(inflated.getName());
+ final PathAttributes attributes = new PathAttributes(file.attributes());
+ if(this.isDirectory(inflated)) {
+ if(Permission.EMPTY != attributes.getPermission()) {
+ final Permission permission = new Permission(attributes.getPermission());
+ permission.setUser(permission.getUser().or(Permission.Action.execute));
+ permission.setGroup(permission.getGroup().or(Permission.Action.execute));
+ permission.setOther(permission.getOther().or(Permission.Action.execute));
+ attributes.setPermission(permission);
+ }
+ // Reset size for folders
+ attributes.setSize(-1L);
+ attributes.setVersionId(null);
+ attributes.setFileId(null);
+ }
+ else {
+ // Translate file size
+ attributes.setSize(this.toCleartextSize(0L, file.attributes().getSize()));
+ }
+ // Add reference to encrypted file
+ attributes.setEncrypted(file);
+ // Add reference for vault
+ attributes.setVaultMetadata(this.getMetadata());
+ final EnumSet type = EnumSet.copyOf(file.getType());
+ type.remove(this.isDirectory(inflated) ? Path.Type.file : Path.Type.directory);
+ type.add(this.isDirectory(inflated) ? Path.Type.directory : Path.Type.file);
+ type.remove(Path.Type.encrypted);
+ type.add(Path.Type.decrypted);
+ final Path decrypted = new Path(file.getParent().attributes().getDecrypted(), cleartextFilename, type, attributes);
+ if(type.contains(Path.Type.symboliclink)) {
+ decrypted.setSymlinkTarget(file.getSymlinkTarget());
+ }
+ return decrypted;
+ }
+ catch(AuthenticationFailedException e) {
+ throw new CryptoAuthenticationException(
+ "Failure to decrypt due to an unauthentic ciphertext", e);
+ }
+ }
+ else {
+ throw new CryptoFilenameMismatchException(
+ String.format("Failure to decrypt %s due to missing pattern match for %s", inflated.getName(), pattern));
+ }
+ }
+
+ private DirectoryContentCryptor.Decrypting getFilenameDecryptor(final Session> session, final Path directory) throws BackgroundException {
+ // Read directory id from file
+ log.debug("Read directory ID from {}", directory);
+ final DirectoryMetadata metadata = this.getDirectoryProvider().getOrCreateDirectoryId(session, directory.getParent());
+ return this.getCryptor().directoryContentCryptor().fileNameDecryptor(metadata);
+ }
+
+ private boolean isDirectory(final Path p) {
+ return p.isDirectory();
+ }
+
+ private Path inflate(final Session> session, final Path file) throws BackgroundException {
+ final String fileName = file.getName();
+ if(this.getFilenameProvider().isDeflated(fileName)) {
+ final String filename = this.getFilenameProvider().inflate(session, fileName);
+ return new Path(file.getParent(), filename, EnumSet.of(Path.Type.file), file.attributes());
+ }
+ return file;
+ }
+
+ public synchronized boolean isUnlocked() {
+ return this.getCryptor() != null;
+ }
+
+ @Override
+ public boolean contains(final Path file) {
+ if(this.isUnlocked()) {
+ return new SimplePathPredicate(file).test(this.getHome()) || file.isChild(this.getHome());
+ }
+ return false;
+ }
+
+ public abstract String getRegularFileExtension();
+
+ public abstract String getDirectoryMetadataFilename();
+
+ public abstract String getBackupDirectoryMetadataFilename();
+
+ public abstract DirectoryMetadata getRootDirId();
+
+ @Override
+ public synchronized void close() {
+ if(this.isUnlocked()) {
+ if(this.getCryptor() != null) {
+ getCryptor().destroy();
+ }
+ if(this.getDirectoryProvider() != null) {
+ this.getDirectoryProvider().destroy();
+ }
+ if(this.getFilenameProvider() != null) {
+ this.getFilenameProvider().destroy();
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getFeature(final Session> session, final Class type, final T delegate) throws UnsupportedException {
+ if(this.isUnlocked()) {
+ if(type == ListService.class) {
+ return (T) new CryptoListService(session, (ListService) delegate, this);
+ }
+ if(type == Touch.class) {
+ // Use default touch feature because touch with remote implementation will not add encrypted file header
+ return (T) new CryptoTouchFeature(session, new DefaultTouchFeature(session), this);
+ }
+ if(type == Directory.class) {
+ //TODO
+ return (T) new CryptoDirectoryV7Feature(session, (Directory) delegate, this);
+// return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+// new CryptoDirectoryV6Feature(session, (Directory) delegate, this) :
+// new CryptoDirectoryV7Feature(session, (Directory) delegate, this)
+// );
+ }
+ if(type == Upload.class) {
+ return (T) new CryptoUploadFeature(session, (Upload) delegate, this);
+ }
+ if(type == Download.class) {
+ return (T) new CryptoDownloadFeature(session, (Download) delegate, this);
+ }
+ if(type == Read.class) {
+ return (T) new CryptoReadFeature(session, (Read) delegate, this);
+ }
+ if(type == Write.class) {
+ return (T) new CryptoWriteFeature(session, (Write) delegate, this);
+ }
+ if(type == MultipartWrite.class) {
+ return (T) new CryptoMultipartWriteFeature(session, (Write) delegate, this);
+ }
+ if(type == Move.class) {
+ //TODO
+ return (T) new CryptoMoveV7Feature(session, (Move) delegate, this);
+// return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+// new CryptoMoveV6Feature(session, (Move) delegate, this) :
+// new CryptoMoveV7Feature(session, (Move) delegate, this));
+
+ }
+ if(type == AttributesFinder.class) {
+ return (T) new CryptoAttributesFeature(session, (AttributesFinder) delegate, this);
+ }
+ if(type == Find.class) {
+ return (T) new CryptoFindFeature(session, (Find) delegate, this);
+ }
+ if(type == UrlProvider.class) {
+ return (T) new CryptoUrlProvider(session, (UrlProvider) delegate, this);
+ }
+ if(type == FileIdProvider.class) {
+ return (T) new CryptoFileIdProvider(session, (FileIdProvider) delegate, this);
+ }
+ if(type == VersionIdProvider.class) {
+ return (T) new CryptoVersionIdProvider(session, (VersionIdProvider) delegate, this);
+ }
+ if(type == Delete.class) {
+ return (T) new CryptoDeleteV7Feature(session, (Delete) delegate, this);
+ //TODO
+// return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+// new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
+// new CryptoDeleteV7Feature(session, (Delete) delegate, this));
+ }
+ if(type == Trash.class) {
+ //TODO
+ return (T) new CryptoDeleteV7Feature(session, (Delete) delegate, this);
+// return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ?
+// new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
+// new CryptoDeleteV7Feature(session, (Delete) delegate, this));
+ }
+ if(type == Symlink.class) {
+ return (T) new CryptoSymlinkFeature(session, (Symlink) delegate, this);
+ }
+ if(type == Headers.class) {
+ return (T) new CryptoHeadersFeature(session, (Headers) delegate, this);
+ }
+ if(type == Compress.class) {
+ return (T) new CryptoCompressFeature(session, (Compress) delegate, this);
+ }
+ if(type == Bulk.class) {
+ return (T) new CryptoBulkFeature(session, (Bulk) delegate, this);
+ }
+ if(type == UnixPermission.class) {
+ return (T) new CryptoUnixPermission(session, (UnixPermission) delegate, this);
+ }
+ if(type == AclPermission.class) {
+ return (T) new CryptoAclPermission(session, (AclPermission) delegate, this);
+ }
+ if(type == Copy.class) {
+ return (T) new CryptoCopyFeature(session, (Copy) delegate, this);
+ }
+ if(type == Timestamp.class) {
+ return (T) new CryptoTimestampFeature(session, (Timestamp) delegate, this);
+ }
+ if(type == Encryption.class) {
+ return (T) new CryptoEncryptionFeature(session, (Encryption) delegate, this);
+ }
+ if(type == Lifecycle.class) {
+ return (T) new CryptoLifecycleFeature(session, (Lifecycle) delegate, this);
+ }
+ if(type == Location.class) {
+ return (T) new CryptoLocationFeature(session, (Location) delegate, this);
+ }
+ if(type == Lock.class) {
+ return (T) new CryptoLockFeature(session, (Lock) delegate, this);
+ }
+ if(type == Logging.class) {
+ return (T) new CryptoLoggingFeature(session, (Logging) delegate, this);
+ }
+ if(type == Redundancy.class) {
+ return (T) new CryptoRedundancyFeature(session, (Redundancy) delegate, this);
+ }
+ if(type == Search.class) {
+ return (T) new CryptoSearchFeature(session, (Search) delegate, this);
+ }
+ if(type == TransferAcceleration.class) {
+ return (T) new CryptoTransferAccelerationFeature<>(session, (TransferAcceleration) delegate, this);
+ }
+ if(type == Versioning.class) {
+ return (T) new CryptoVersioningFeature(session, (Versioning) delegate, this);
+ }
+ }
+ return delegate;
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java
index 21282729888..452af7e12b5 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/ContentReader.java
@@ -21,10 +21,12 @@
import ch.cyberduck.core.Session;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Read;
+import ch.cyberduck.core.io.StreamCopier;
import ch.cyberduck.core.transfer.TransferStatus;
import org.apache.commons.io.IOUtils;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -49,6 +51,19 @@ public String read(final Path file) throws BackgroundException {
}
}
+ public byte[] readBytes(final Path file) throws BackgroundException {
+ final Read read = session._getFeature(Read.class);
+ final TransferStatus status = new TransferStatus().setLength(file.attributes().getSize());
+ try (final InputStream in = read.read(file, status, new DisabledConnectionCallback())) {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ new StreamCopier(status, status).transfer(in, out);
+ return out.toByteArray();
+ }
+ catch(IOException e) {
+ throw new DefaultIOExceptionMappingService().map(e);
+ }
+ }
+
public Reader getReader(final Path file) throws BackgroundException {
final Read read = session._getFeature(Read.class);
return new InputStreamReader(read.read(file, new TransferStatus().setLength(file.attributes().getSize()), new DisabledConnectionCallback()), StandardCharsets.UTF_8);
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java
index 4b31674db9b..7bcac44e2f3 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java
@@ -22,16 +22,15 @@
import ch.cyberduck.core.features.AclPermission;
import ch.cyberduck.core.transfer.TransferStatus;
-import java.util.EnumSet;
import java.util.List;
public class CryptoAclPermission implements AclPermission {
private final Session> session;
private final AclPermission delegate;
- private final CryptoVault cryptomator;
+ private final AbstractVault cryptomator;
- public CryptoAclPermission(final Session> session, final AclPermission delegate, final CryptoVault cryptomator) {
+ public CryptoAclPermission(final Session> session, final AclPermission delegate, final AbstractVault cryptomator) {
this.session = session;
this.delegate = delegate;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java
index 88ccb32e736..b61023a58d5 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java
@@ -19,6 +19,8 @@
import ch.cyberduck.core.Session;
import ch.cyberduck.core.exception.BackgroundException;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+
import java.util.EnumSet;
public interface CryptoDirectory {
@@ -27,21 +29,20 @@ public interface CryptoDirectory {
* Get encrypted filename for given clear text filename with id of parent encrypted directory.
*
* @param session Connection
- * @param directoryId Parent folder directory id
+ * @param parent Parent folder
* @param filename Clear text filename
* @param type File type
* @return Encrypted filename
*/
- String toEncrypted(Session> session, String directoryId, String filename, EnumSet type) throws BackgroundException;
+ String toEncrypted(Session> session, Path parent, String filename, EnumSet type) throws BackgroundException;
/**
* Get encrypted reference for clear text directory path.
*
- * @param session Connection
- * @param directoryId Directory ID or null to read directory id from metadata file
- * @param directory Clear text
+ * @param session Connection
+ * @param directory Clear text
*/
- Path toEncrypted(Session> session, String directoryId, Path directory) throws BackgroundException;
+ Path toEncrypted(Session> session, Path directory) throws BackgroundException;
/**
* Remove from cache
@@ -49,4 +50,8 @@ public interface CryptoDirectory {
void delete(Path directory);
void destroy();
+
+ DirectoryMetadata getOrCreateDirectoryId(Session> session, Path directory) throws BackgroundException;
+
+ DirectoryMetadata createDirectoryId(final Path directory);
}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java
index bcb52f32f5f..8cc455558bc 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java
@@ -27,9 +27,9 @@
public class CryptoTransferStatus extends ProxyTransferStatus implements StreamCancelation, StreamProgress {
private static final Logger log = LogManager.getLogger(CryptoTransferStatus.class);
- private final CryptoVault vault;
+ private final AbstractVault vault;
- public CryptoTransferStatus(final CryptoVault vault, final TransferStatus proxy) {
+ public CryptoTransferStatus(final AbstractVault vault, final TransferStatus proxy) {
super(proxy);
this.vault = vault;
this.setLength(vault.toCiphertextSize(proxy.getOffset(), proxy.getLength()))
@@ -42,7 +42,7 @@ public CryptoTransferStatus(final CryptoVault vault, final TransferStatus proxy)
public TransferStatus setResponse(final PathAttributes attributes) {
try {
attributes.setSize(vault.toCleartextSize(0L, attributes.getSize()));
- attributes.setVault(vault.getHome());
+ attributes.setVaultMetadata(vault.getMetadata());
super.setResponse(attributes);
}
catch(CryptoInvalidFilesizeException e) {
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java
deleted file mode 100644
index 57c45623467..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java
+++ /dev/null
@@ -1,853 +0,0 @@
-package ch.cyberduck.core.cryptomator;
-
-/*
- * Copyright (c) 2002-2016 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.*;
-import ch.cyberduck.core.cryptomator.features.*;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV6Provider;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
-import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV6Provider;
-import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
-import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.LocalAccessDeniedException;
-import ch.cyberduck.core.exception.LoginCanceledException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.features.*;
-import ch.cyberduck.core.preferences.Preferences;
-import ch.cyberduck.core.preferences.PreferencesFactory;
-import ch.cyberduck.core.shared.DefaultTouchFeature;
-import ch.cyberduck.core.shared.DefaultUrlProvider;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.DefaultVaultRegistry;
-import ch.cyberduck.core.vault.VaultCredentials;
-import ch.cyberduck.core.vault.VaultException;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.cryptomator.cryptolib.api.AuthenticationFailedException;
-import org.cryptomator.cryptolib.api.Cryptor;
-import org.cryptomator.cryptolib.api.CryptorProvider;
-import org.cryptomator.cryptolib.api.FileContentCryptor;
-import org.cryptomator.cryptolib.api.FileHeaderCryptor;
-import org.cryptomator.cryptolib.api.InvalidPassphraseException;
-import org.cryptomator.cryptolib.api.Masterkey;
-import org.cryptomator.cryptolib.common.MasterkeyFile;
-import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.text.MessageFormat;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.auth0.jwt.exceptions.InvalidClaimException;
-import com.auth0.jwt.exceptions.JWTVerificationException;
-import com.auth0.jwt.exceptions.SignatureVerificationException;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import com.google.common.io.BaseEncoding;
-import com.google.gson.JsonParseException;
-
-import static ch.cyberduck.core.vault.DefaultVaultRegistry.DEFAULT_VAULTCONFIG_FILE_NAME;
-
-/**
- * Cryptomator vault implementation
- */
-public class CryptoVault implements Vault {
- private static final Logger log = LogManager.getLogger(CryptoVault.class);
-
- public static final int VAULT_VERSION_DEPRECATED = 6;
- public static final int VAULT_VERSION = PreferencesFactory.get().getInteger("cryptomator.vault.version");
- public static final byte[] VAULT_PEPPER = PreferencesFactory.get().getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8);
-
- public static final String DIR_PREFIX = "0";
-
- private static final Pattern BASE32_PATTERN = Pattern.compile("^0?(([A-Z2-7]{8})*[A-Z2-7=]{8})");
- private static final Pattern BASE64URL_PATTERN = Pattern.compile("^([A-Za-z0-9_=-]+).c9r");
-
- private static final String JSON_KEY_VAULTVERSION = "format";
- private static final String JSON_KEY_CIPHERCONFIG = "cipherCombo";
- private static final String JSON_KEY_SHORTENING_THRESHOLD = "shorteningThreshold";
-
- /**
- * Root of vault directory
- */
- private final Path home;
- private final Path masterkey;
-
- private final Path config;
- private final Path vault;
- private int vaultVersion;
- private int nonceSize;
-
- private final PasswordStore keychain = PasswordStoreFactory.get();
- private final Preferences preferences = PreferencesFactory.get();
- private Cryptor cryptor;
- private CryptorCache fileNameCryptor;
-
- private CryptoFilename filenameProvider;
- private CryptoDirectory directoryProvider;
-
- private final byte[] pepper;
-
- public CryptoVault(final Path home) {
- this(home, DefaultVaultRegistry.DEFAULT_MASTERKEY_FILE_NAME, DEFAULT_VAULTCONFIG_FILE_NAME, VAULT_PEPPER);
- }
-
- public CryptoVault(final Path home, final String masterkey, final String config, final byte[] pepper) {
- this.home = home;
- this.masterkey = new Path(home, masterkey, EnumSet.of(Path.Type.file, Path.Type.vault));
- this.config = new Path(home, config, EnumSet.of(Path.Type.file, Path.Type.vault));
- this.pepper = pepper;
- // New vault home with vault flag set for internal use
- final EnumSet type = EnumSet.copyOf(home.getType());
- type.add(Path.Type.vault);
- if(home.isRoot()) {
- this.vault = new Path(home.getAbsolute(), type, new PathAttributes(home.attributes()));
- }
- else {
- this.vault = new Path(home.getParent(), home.getName(), type, new PathAttributes(home.attributes()));
- }
- }
-
- public synchronized Path create(final Session> session, final VaultCredentials credentials, final int version) throws BackgroundException {
- return this.create(session, null, credentials, version);
- }
-
- public synchronized Path create(final Session> session, final String region, final VaultCredentials credentials, final int version) throws BackgroundException {
- final Host bookmark = session.getHost();
- if(credentials.isSaved()) {
- try {
- keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
- new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
- }
- catch(LocalAccessDeniedException e) {
- log.error("Failure {} saving credentials for {} in password store", e, bookmark);
- }
- }
- final String passphrase = credentials.getPassword();
- final ByteArrayOutputStream mkArray = new ByteArrayOutputStream();
- final Masterkey mk = Masterkey.generate(FastSecureRandomProvider.get().provide());
- final MasterkeyFileAccess access = new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide());
- final MasterkeyFile masterkeyFile;
- try {
- access.persist(mk, mkArray, passphrase, version);
- masterkeyFile = MasterkeyFile.read(new StringReader(new String(mkArray.toByteArray(), StandardCharsets.UTF_8)));
- }
- catch(IOException e) {
- throw new VaultException("Failure creating master key", e);
- }
- log.debug("Write master key to {}", masterkey);
- // Obtain non encrypted directory writer
- final Directory> directory = session._getFeature(Directory.class);
- final TransferStatus status = new TransferStatus().setRegion(region);
- final Encryption encryption = session._getFeature(Encryption.class);
- if(encryption != null) {
- status.setEncryption(encryption.getDefault(home));
- }
- final Path vault = directory.mkdir(session._getFeature(Write.class), home, status);
- new ContentWriter(session).write(masterkey, mkArray.toByteArray());
- if(VAULT_VERSION == version) {
- // Create vaultconfig.cryptomator
- final Algorithm algorithm = Algorithm.HMAC256(mk.getEncoded());
- final String conf = JWT.create()
- .withJWTId(new UUIDRandomStringService().random())
- .withKeyId(String.format("masterkeyfile:%s", masterkey.getName()))
- .withClaim(JSON_KEY_VAULTVERSION, version)
- .withClaim(JSON_KEY_CIPHERCONFIG, CryptorProvider.Scheme.SIV_GCM.toString())
- .withClaim(JSON_KEY_SHORTENING_THRESHOLD, CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD)
- .sign(algorithm);
- new ContentWriter(session).write(config, conf.getBytes(StandardCharsets.US_ASCII));
- this.open(parseVaultConfigFromJWT(conf).withMasterkeyFile(masterkeyFile), passphrase);
- }
- else {
- this.open(new VaultConfig(version, CryptoFilenameV6Provider.DEFAULT_NAME_SHORTENING_THRESHOLD,
- CryptorProvider.Scheme.SIV_CTRMAC, null, null).withMasterkeyFile(masterkeyFile), passphrase);
- }
- final Path secondLevel = directoryProvider.toEncrypted(session, home.attributes().getDirectoryId(), home);
- final Path firstLevel = secondLevel.getParent();
- final Path dataDir = firstLevel.getParent();
- log.debug("Create vault root directory at {}", secondLevel);
- directory.mkdir(session._getFeature(Write.class), dataDir, status);
- directory.mkdir(session._getFeature(Write.class), firstLevel, status);
- directory.mkdir(session._getFeature(Write.class), secondLevel, status);
- return vault;
- }
-
- @Override
- public synchronized Path create(final Session> session, final String region, final VaultCredentials credentials) throws BackgroundException {
- return this.create(session, region, credentials, VAULT_VERSION);
- }
-
- @Override
- public synchronized CryptoVault load(final Session> session, final PasswordCallback prompt) throws BackgroundException {
- if(this.isUnlocked()) {
- log.warn("Skip unlock of open vault {}", this);
- return this;
- }
- final Host bookmark = session.getHost();
- String passphrase = keychain.getPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
- new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
- if(null == passphrase) {
- // Legacy
- passphrase = keychain.getPassword(String.format("Cryptomator Passphrase %s", bookmark.getHostname()),
- new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
- }
- return this.unlock(session, prompt, bookmark, passphrase);
- }
-
- private VaultConfig readVaultConfig(final Session> session) throws BackgroundException {
- final MasterkeyFile masterkeyFile = this.readMasterkeyFile(session, masterkey);
- try {
- return parseVaultConfigFromJWT(new ContentReader(session).read(config))
- .withMasterkeyFile(masterkeyFile);
- }
- catch(NotfoundException e) {
- log.debug("Ignore failure reading vault configuration {}", config);
- return parseVaultConfigFromMasterKey(masterkeyFile)
- .withMasterkeyFile(masterkeyFile);
- }
- }
-
- private static VaultConfig parseVaultConfigFromMasterKey(final MasterkeyFile masterkeyFile) {
- return new VaultConfig(masterkeyFile.version,
- masterkeyFile.version == VAULT_VERSION_DEPRECATED ?
- CryptoFilenameV6Provider.DEFAULT_NAME_SHORTENING_THRESHOLD :
- CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD,
- CryptorProvider.Scheme.SIV_CTRMAC, null, null);
- }
-
-
- private static VaultConfig parseVaultConfigFromJWT(final String token) {
- final DecodedJWT decoded = JWT.decode(token);
- return new VaultConfig(
- decoded.getClaim(JSON_KEY_VAULTVERSION).asInt(),
- decoded.getClaim(JSON_KEY_SHORTENING_THRESHOLD).asInt(),
- CryptorProvider.Scheme.valueOf(decoded.getClaim(JSON_KEY_CIPHERCONFIG).asString()),
- decoded.getAlgorithm(), decoded);
- }
-
- private MasterkeyFile readMasterkeyFile(final Session> session, final Path file) throws BackgroundException {
- log.debug("Read master key {}", file);
- try(Reader reader = new ContentReader(session).getReader(file)) {
- return MasterkeyFile.read(reader);
- }
- catch(JsonParseException | IllegalArgumentException | IllegalStateException | IOException e) {
- throw new VaultException(String.format("Failure reading vault master key file %s", file.getName()), e);
- }
- }
-
- public CryptoVault unlock(final Session> session, final PasswordCallback prompt, final Host bookmark, final String passphrase) throws BackgroundException {
- final VaultConfig vaultConfig = this.readVaultConfig(session);
- this.unlock(vaultConfig, passphrase, bookmark, prompt,
- MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())
- );
- return this;
- }
-
- public void unlock(final VaultConfig vaultConfig, final String passphrase, final Host bookmark, final PasswordCallback prompt,
- final String message) throws BackgroundException {
- final Credentials credentials;
- if(null == passphrase) {
- credentials = prompt.prompt(
- bookmark, LocaleFactory.localizedString("Unlock Vault", "Cryptomator"),
- message,
- new LoginOptions()
- .save(preferences.getBoolean("cryptomator.vault.keychain"))
- .user(false)
- .anonymous(false)
- .icon("cryptomator.tiff")
- .passwordPlaceholder(LocaleFactory.localizedString("Passphrase", "Cryptomator")));
- if(null == credentials.getPassword()) {
- throw new LoginCanceledException();
- }
- }
- else {
- credentials = new VaultCredentials(passphrase).setSaved(false);
- }
- try {
- this.open(vaultConfig, credentials.getPassword());
- if(credentials.isSaved()) {
- log.info("Save passphrase for {}", masterkey);
- // Save password with hostname and path to masterkey.cryptomator in keychain
- keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
- new DefaultUrlProvider(bookmark).toUrl(masterkey, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
- }
- }
- catch(CryptoAuthenticationException e) {
- this.unlock(vaultConfig, null, bookmark, prompt, String.format("%s %s.", e.getDetail(),
- MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())));
- }
- }
-
- @Override
- public synchronized void close() {
- if(this.isUnlocked()) {
- log.info("Close vault with cryptor {}", cryptor);
- if(cryptor != null) {
- cryptor.destroy();
- }
- if(directoryProvider != null) {
- directoryProvider.destroy();
- }
- if(filenameProvider != null) {
- filenameProvider.destroy();
- }
- }
- cryptor = null;
- fileNameCryptor = null;
- }
-
- protected CryptoFilename createFilenameProvider(final VaultConfig vaultConfig) {
- switch(vaultConfig.version) {
- case VAULT_VERSION_DEPRECATED:
- return new CryptoFilenameV6Provider(vault);
- default:
- return new CryptoFilenameV7Provider(vaultConfig.getShorteningThreshold());
- }
- }
-
- protected CryptoDirectory createDirectoryProvider(final VaultConfig vaultConfig) {
- switch(vaultConfig.version) {
- case VAULT_VERSION_DEPRECATED:
- return new CryptoDirectoryV6Provider(vault, this);
- default:
- return new CryptoDirectoryV7Provider(vault, this);
- }
- }
-
- protected void open(final VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException {
- this.open(vaultConfig, passphrase, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig));
- }
-
- protected void open(final VaultConfig vaultConfig, final CharSequence passphrase,
- final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException {
- try {
- final Masterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase);
- this.open(vaultConfig, masterKey, filenameProvider, directoryProvider);
- }
- catch(IllegalArgumentException | IOException e) {
- throw new VaultException("Failure reading key file", e);
- }
- catch(InvalidPassphraseException e) {
- throw new CryptoAuthenticationException("Failure to decrypt master key file", e);
- }
- }
-
- protected void open(final VaultConfig vaultConfig, final Masterkey masterKey) throws BackgroundException {
- this.open(vaultConfig, masterKey, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig));
- }
-
- protected void open(final VaultConfig vaultConfig, final Masterkey masterKey,
- final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException {
- this.vaultVersion = vaultConfig.version;
- final CryptorProvider provider = CryptorProvider.forScheme(vaultConfig.getCipherCombo());
- log.debug("Initialized crypto provider {}", provider);
- vaultConfig.verify(masterKey.getEncoded(), VAULT_VERSION);
- this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide());
- this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor());
- this.filenameProvider = filenameProvider;
- this.directoryProvider = directoryProvider;
- this.nonceSize = vaultConfig.getNonceSize();
- }
-
- private Masterkey getMasterKey(final MasterkeyFile mkFile, final CharSequence passphrase) throws IOException {
- final StringWriter writer = new StringWriter();
- mkFile.write(writer);
- return new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide()).load(
- new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase);
- }
-
- public synchronized boolean isUnlocked() {
- return cryptor != null;
- }
-
- @Override
- public State getState() {
- return this.isUnlocked() ? State.open : State.closed;
- }
-
- @Override
- public boolean contains(final Path file) {
- return new SimplePathPredicate(file).test(home) || file.isChild(home);
- }
-
- @Override
- public Path encrypt(final Session> session, final Path file) throws BackgroundException {
- return this.encrypt(session, file, file.attributes().getDirectoryId(), false);
- }
-
- @Override
- public Path encrypt(final Session> session, final Path file, boolean metadata) throws BackgroundException {
- return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata);
- }
-
- public Path encrypt(final Session> session, final Path file, final String directoryId, boolean metadata) throws BackgroundException {
- final Path encrypted;
- if(file.isFile() || metadata) {
- if(file.getType().contains(Path.Type.vault)) {
- log.warn("Skip file {} because it is marked as an internal vault path", file);
- return file;
- }
- if(new SimplePathPredicate(file).test(home)) {
- log.warn("Skip vault home {} because the root has no metadata file", file);
- return file;
- }
- final Path parent;
- final String filename;
- if(file.getType().contains(Path.Type.encrypted)) {
- final Path decrypted = file.attributes().getDecrypted();
- parent = directoryProvider.toEncrypted(session, decrypted.getParent().attributes().getDirectoryId(), decrypted.getParent());
- filename = directoryProvider.toEncrypted(session, parent.attributes().getDirectoryId(), decrypted.getName(), decrypted.getType());
- }
- else {
- parent = directoryProvider.toEncrypted(session, file.getParent().attributes().getDirectoryId(), file.getParent());
- filename = directoryProvider.toEncrypted(session, parent.attributes().getDirectoryId(), file.getName(), file.getType());
- }
- final PathAttributes attributes = new PathAttributes(file.attributes());
- attributes.setDirectoryId(null);
- if(!file.isFile() && !metadata) {
- // The directory is different from the metadata file used to resolve the actual folder
- attributes.setVersionId(null);
- attributes.setFileId(null);
- }
- // Translate file size
- attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize()));
- final EnumSet type = EnumSet.copyOf(file.getType());
- if(metadata && vaultVersion == VAULT_VERSION_DEPRECATED) {
- type.remove(Path.Type.directory);
- type.add(Path.Type.file);
- }
- type.remove(Path.Type.decrypted);
- type.add(Path.Type.encrypted);
- encrypted = new Path(parent, filename, type, attributes);
- }
- else {
- if(file.getType().contains(Path.Type.encrypted)) {
- log.warn("Skip file {} because it is already marked as an encrypted path", file);
- return file;
- }
- if(file.getType().contains(Path.Type.vault)) {
- return directoryProvider.toEncrypted(session, home.attributes().getDirectoryId(), home);
- }
- encrypted = directoryProvider.toEncrypted(session, directoryId, file);
- }
- // Add reference to decrypted file
- if(!file.getType().contains(Path.Type.encrypted)) {
- encrypted.attributes().setDecrypted(file);
- }
- // Add reference for vault
- file.attributes().setVault(home);
- encrypted.attributes().setVault(home);
- return encrypted;
- }
-
- @Override
- public Path decrypt(final Session> session, final Path file) throws BackgroundException {
- if(file.getType().contains(Path.Type.decrypted)) {
- log.warn("Skip file {} because it is already marked as an decrypted path", file);
- return file;
- }
- if(file.getType().contains(Path.Type.vault)) {
- log.warn("Skip file {} because it is marked as an internal vault path", file);
- return file;
- }
- final Path inflated = this.inflate(session, file);
- final Pattern pattern = vaultVersion == VAULT_VERSION_DEPRECATED ? BASE32_PATTERN : BASE64URL_PATTERN;
- final Matcher m = pattern.matcher(inflated.getName());
- if(m.matches()) {
- final String ciphertext = m.group(1);
- try {
- final String cleartextFilename = fileNameCryptor.decryptFilename(
- vaultVersion == VAULT_VERSION_DEPRECATED ? BaseEncoding.base32() : BaseEncoding.base64Url(),
- ciphertext, file.getParent().attributes().getDirectoryId().getBytes(StandardCharsets.UTF_8));
- final PathAttributes attributes = new PathAttributes(file.attributes());
- if(this.isDirectory(inflated)) {
- if(Permission.EMPTY != attributes.getPermission()) {
- final Permission permission = new Permission(attributes.getPermission());
- permission.setUser(permission.getUser().or(Permission.Action.execute));
- permission.setGroup(permission.getGroup().or(Permission.Action.execute));
- permission.setOther(permission.getOther().or(Permission.Action.execute));
- attributes.setPermission(permission);
- }
- // Reset size for folders
- attributes.setSize(-1L);
- attributes.setVersionId(null);
- attributes.setFileId(null);
- }
- else {
- // Translate file size
- attributes.setSize(this.toCleartextSize(0L, file.attributes().getSize()));
- }
- // Add reference to encrypted file
- attributes.setEncrypted(file);
- // Add reference for vault
- attributes.setVault(home);
- final EnumSet type = EnumSet.copyOf(file.getType());
- type.remove(this.isDirectory(inflated) ? Path.Type.file : Path.Type.directory);
- type.add(this.isDirectory(inflated) ? Path.Type.directory : Path.Type.file);
- type.remove(Path.Type.encrypted);
- type.add(Path.Type.decrypted);
- final Path decrypted = new Path(file.getParent().attributes().getDecrypted(), cleartextFilename, type, attributes);
- if(type.contains(Path.Type.symboliclink)) {
- decrypted.setSymlinkTarget(file.getSymlinkTarget());
- }
- return decrypted;
- }
- catch(AuthenticationFailedException e) {
- throw new CryptoAuthenticationException(
- "Failure to decrypt due to an unauthentic ciphertext", e);
- }
- }
- else {
- throw new CryptoFilenameMismatchException(
- String.format("Failure to decrypt %s due to missing pattern match for %s", inflated.getName(), pattern));
- }
- }
-
- private boolean isDirectory(final Path p) {
- if(vaultVersion == VAULT_VERSION_DEPRECATED) {
- return p.getName().startsWith(DIR_PREFIX);
- }
- return p.isDirectory();
- }
-
- @Override
- public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) {
- if(TransferStatus.UNKNOWN_LENGTH == cleartextFileSize) {
- return TransferStatus.UNKNOWN_LENGTH;
- }
- final int headerSize;
- if(0L == cleartextFileOffset) {
- headerSize = cryptor.fileHeaderCryptor().headerSize();
- }
- else {
- headerSize = 0;
- }
- return headerSize + cryptor.fileContentCryptor().ciphertextSize(cleartextFileSize);
- }
-
- @Override
- public long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws CryptoInvalidFilesizeException {
- if(TransferStatus.UNKNOWN_LENGTH == ciphertextFileSize) {
- return TransferStatus.UNKNOWN_LENGTH;
- }
- final int headerSize;
- if(0L == cleartextFileOffset) {
- headerSize = cryptor.fileHeaderCryptor().headerSize();
- }
- else {
- headerSize = 0;
- }
- try {
- return cryptor.fileContentCryptor().cleartextSize(ciphertextFileSize - headerSize);
- }
- catch(AssertionError e) {
- throw new CryptoInvalidFilesizeException(String.format("Encrypted file size must be at least %d bytes", headerSize));
- }
- catch(IllegalArgumentException e) {
- throw new CryptoInvalidFilesizeException(String.format("Invalid file size. %s", e.getMessage()));
- }
- }
-
- private Path inflate(final Session> session, final Path file) throws BackgroundException {
- final String fileName = file.getName();
- if(filenameProvider.isDeflated(fileName)) {
- final String filename = filenameProvider.inflate(session, fileName);
- return new Path(file.getParent(), filename, EnumSet.of(Path.Type.file), file.attributes());
- }
- return file;
- }
-
- public Path getHome() {
- return home;
- }
-
- public Path getMasterkey() {
- return masterkey;
- }
-
- public Path getConfig() {
- return config;
- }
-
- public FileHeaderCryptor getFileHeaderCryptor() {
- return cryptor.fileHeaderCryptor();
- }
-
- public FileContentCryptor getFileContentCryptor() {
- return cryptor.fileContentCryptor();
- }
-
- public CryptorCache getFileNameCryptor() {
- return fileNameCryptor;
- }
-
- public CryptoFilename getFilenameProvider() {
- return filenameProvider;
- }
-
- public CryptoDirectory getDirectoryProvider() {
- return directoryProvider;
- }
-
- public int getNonceSize() {
- return nonceSize;
- }
-
- public int numberOfChunks(final long cleartextFileSize) {
- return (int) (cleartextFileSize / cryptor.fileContentCryptor().cleartextChunkSize() +
- ((cleartextFileSize % cryptor.fileContentCryptor().cleartextChunkSize() > 0) ? 1 : 0));
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public T getFeature(final Session> session, final Class type, final T delegate) {
- if(this.isUnlocked()) {
- if(type == ListService.class) {
- return (T) new CryptoListService(session, (ListService) delegate, this);
- }
- if(type == Touch.class) {
- // Use default touch feature because touch with remote implementation will not add encrypted file header
- return (T) new CryptoTouchFeature(session, new DefaultTouchFeature(session), this);
- }
- if(type == Directory.class) {
- return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
- new CryptoDirectoryV6Feature(session, (Directory) delegate, this) :
- new CryptoDirectoryV7Feature(session, (Directory) delegate, this)
- );
- }
- if(type == Upload.class) {
- return (T) new CryptoUploadFeature(session, (Upload) delegate, this);
- }
- if(type == Download.class) {
- return (T) new CryptoDownloadFeature(session, (Download) delegate, this);
- }
- if(type == Read.class) {
- return (T) new CryptoReadFeature(session, (Read) delegate, this);
- }
- if(type == Write.class) {
- return (T) new CryptoWriteFeature(session, (Write) delegate, this);
- }
- if(type == MultipartWrite.class) {
- return (T) new CryptoMultipartWriteFeature(session, (Write) delegate, this);
- }
- if(type == Move.class) {
- return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
- new CryptoMoveV6Feature(session, (Move) delegate, this) :
- new CryptoMoveV7Feature(session, (Move) delegate, this));
-
- }
- if(type == AttributesFinder.class) {
- return (T) new CryptoAttributesFeature(session, (AttributesFinder) delegate, this);
- }
- if(type == Find.class) {
- return (T) new CryptoFindFeature(session, (Find) delegate, this);
- }
- if(type == UrlProvider.class) {
- return (T) new CryptoUrlProvider(session, (UrlProvider) delegate, this);
- }
- if(type == FileIdProvider.class) {
- return (T) new CryptoFileIdProvider(session, (FileIdProvider) delegate, this);
- }
- if(type == VersionIdProvider.class) {
- return (T) new CryptoVersionIdProvider(session, (VersionIdProvider) delegate, this);
- }
- if(type == Delete.class) {
- return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
- new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
- new CryptoDeleteV7Feature(session, (Delete) delegate, this));
- }
- if(type == Trash.class) {
- return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ?
- new CryptoDeleteV6Feature(session, (Delete) delegate, this) :
- new CryptoDeleteV7Feature(session, (Delete) delegate, this));
- }
- if(type == Symlink.class) {
- return (T) new CryptoSymlinkFeature(session, (Symlink) delegate, this);
- }
- if(type == Headers.class) {
- return (T) new CryptoHeadersFeature(session, (Headers) delegate, this);
- }
- if(type == Compress.class) {
- return (T) new CryptoCompressFeature(session, (Compress) delegate, this);
- }
- if(type == Bulk.class) {
- return (T) new CryptoBulkFeature(session, (Bulk) delegate, this);
- }
- if(type == UnixPermission.class) {
- return (T) new CryptoUnixPermission(session, (UnixPermission) delegate, this);
- }
- if(type == AclPermission.class) {
- return (T) new CryptoAclPermission(session, (AclPermission) delegate, this);
- }
- if(type == Copy.class) {
- return (T) new CryptoCopyFeature(session, (Copy) delegate, this);
- }
- if(type == Timestamp.class) {
- return (T) new CryptoTimestampFeature(session, (Timestamp) delegate, this);
- }
- if(type == Encryption.class) {
- return (T) new CryptoEncryptionFeature(session, (Encryption) delegate, this);
- }
- if(type == Lifecycle.class) {
- return (T) new CryptoLifecycleFeature(session, (Lifecycle) delegate, this);
- }
- if(type == Location.class) {
- return (T) new CryptoLocationFeature(session, (Location) delegate, this);
- }
- if(type == Lock.class) {
- return (T) new CryptoLockFeature(session, (Lock) delegate, this);
- }
- if(type == Logging.class) {
- return (T) new CryptoLoggingFeature(session, (Logging) delegate, this);
- }
- if(type == Redundancy.class) {
- return (T) new CryptoRedundancyFeature(session, (Redundancy) delegate, this);
- }
- if(type == Search.class) {
- return (T) new CryptoSearchFeature(session, (Search) delegate, this);
- }
- if(type == TransferAcceleration.class) {
- return (T) new CryptoTransferAccelerationFeature<>(session, (TransferAcceleration) delegate, this);
- }
- if(type == Versioning.class) {
- return (T) new CryptoVersioningFeature(session, (Versioning) delegate, this);
- }
- }
- return delegate;
- }
-
- @Override
- public boolean equals(final Object o) {
- if(this == o) {
- return true;
- }
- if(!(o instanceof CryptoVault)) {
- return false;
- }
- final CryptoVault that = (CryptoVault) o;
- return new SimplePathPredicate(home).test(that.home);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(new SimplePathPredicate(home));
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("CryptoVault{");
- sb.append("home=").append(home);
- sb.append(", cryptor=").append(cryptor);
- sb.append('}');
- return sb.toString();
- }
-
- public static class VaultConfig {
-
- private final int version;
- private final int shorteningThreshold;
- private final CryptorProvider.Scheme cipherCombo;
- private final String algorithm;
- private final DecodedJWT token;
- private MasterkeyFile mkfile;
-
- public VaultConfig(int version, int shorteningThreshold, CryptorProvider.Scheme cipherCombo, String algorithm, DecodedJWT token) {
- this.version = version;
- this.shorteningThreshold = shorteningThreshold;
- this.cipherCombo = cipherCombo;
- this.algorithm = algorithm;
- this.token = token;
- }
-
- public int vaultVersion() {
- return version;
- }
-
- public VaultConfig withMasterkeyFile(final MasterkeyFile mkfile) {
- this.mkfile = mkfile;
- return this;
- }
-
- public MasterkeyFile getMkfile() {
- return mkfile;
- }
-
- public int getShorteningThreshold() {
- return shorteningThreshold;
- }
-
- public CryptorProvider.Scheme getCipherCombo() {
- return cipherCombo;
- }
-
- public int getNonceSize() throws VaultException {
- switch(cipherCombo) {
- case SIV_CTRMAC:
- return 16;
- case SIV_GCM:
- return 12;
- default:
- throw new VaultException(String.format("Unsupported cipher scheme %s", cipherCombo));
- }
- }
-
- private Algorithm initAlgorithm(byte[] rawKey) throws VaultException {
- switch(algorithm) {
- case "HS256":
- return Algorithm.HMAC256(rawKey);
- case "HS384":
- return Algorithm.HMAC384(rawKey);
- case "HS512":
- return Algorithm.HMAC512(rawKey);
- default:
- throw new VaultException(String.format("Unsupported signature algorithm %s", algorithm));
- }
- }
-
- public void verify(byte[] rawKey, int expectedVaultVersion) throws VaultException {
- try {
- if(token == null) {
- return;
- }
- JWTVerifier verifier = JWT.require(initAlgorithm(rawKey))
- .withClaim(JSON_KEY_VAULTVERSION, expectedVaultVersion)
- .build();
- verifier.verify(token);
- }
- catch(SignatureVerificationException e) {
- throw new VaultException("Invalid JWT signature", e);
- }
- catch(InvalidClaimException e) {
- throw new VaultException(String.format("Expected vault config for version %d", expectedVaultVersion), e);
- }
- catch(JWTVerificationException e) {
- throw new VaultException(String.format("Failed to verify vault config %s", token), e);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultProvider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultProvider.java
new file mode 100644
index 00000000000..ee6c59a04c3
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultProvider.java
@@ -0,0 +1,110 @@
+package ch.cyberduck.core.cryptomator;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.ListProgressListener;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.impl.uvf.DefaultVaultMetadataUVFProvider;
+import ch.cyberduck.core.cryptomator.impl.v8.DefaultVaultMetadataV8Provider;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.features.Find;
+import ch.cyberduck.core.preferences.HostPreferencesFactory;
+import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultProvider;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Cryptomator vault implementation
+ */
+public class CryptoVaultProvider implements VaultProvider {
+ private static final Logger log = LogManager.getLogger(CryptoVaultProvider.class);
+
+ private final Map markers;
+
+ public CryptoVaultProvider(final Session> session) {
+ this.markers = ImmutableMap.of(
+ HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.masterkey.filename"), VaultMetadata.Type.V8,
+ HostPreferencesFactory.get(session.getHost()).getProperty("cryptomator.vault.config.filename.uvf"), VaultMetadata.Type.UVF
+ );
+ }
+
+ @Override
+ public boolean isVault(final Path path) {
+ return markers.keySet().stream().anyMatch(marker -> path.getName().equals(marker));
+ }
+
+ @Override
+ public VaultMetadata metadata(final Path path) {
+ if(this.isVault(path)) {
+ return new VaultMetadata(path.getParent(), markers.get(path.getName()));
+ }
+ return null;
+ }
+
+ @Override
+ public VaultMetadata find(final Path directory, final Find find, final ListProgressListener listener) throws BackgroundException {
+ for(String marker : markers.keySet()) {
+ final Path m = new Path(directory, marker, EnumSet.of(Path.Type.file));
+ if(find.find(m, listener)) {
+ return new VaultMetadata(m.getParent(), markers.get(marker));
+ }
+ }
+ return null;
+ }
+
+ //TODO prompt parameter wegnehmen?
+ @Override
+ public synchronized AbstractVault provide(final Session> session, final VaultMetadata metadata) {
+ switch(metadata.type) {
+ case V8:
+ return new ch.cyberduck.core.cryptomator.impl.v8.CryptoVault(metadata.root);
+ case UVF:
+ return new ch.cyberduck.core.cryptomator.impl.uvf.CryptoVault(metadata.root);
+ default:
+ log.error("Unknown vault type {}", metadata.type);
+ // TODO schmeissen, DISABLED zurück geben geht nicht weil kein AbstractVault
+ // throw new ...
+ return null;
+ }
+ }
+
+ //TODO create methode braucht es glaube ich nicht unbedingt
+
+ @Override
+ public AbstractVault create(final Session> session, final String region, final VaultCredentials credentials, final VaultMetadata metadata) throws BackgroundException {
+ switch(metadata.type) {
+ case V8:
+ return new ch.cyberduck.core.cryptomator.impl.v8.CryptoVault(metadata.root).create(session, region, new DefaultVaultMetadataV8Provider(credentials));
+ case UVF:
+ //TODO plain UVF
+ return new ch.cyberduck.core.cryptomator.impl.uvf.CryptoVault(metadata.root).create(session, region, new DefaultVaultMetadataUVFProvider());
+ default:
+ log.error("Unknown vault type {}", metadata.type);
+ // TODO schmeissen, DISABLED zurück geben geht nicht weil kein AbstractVault
+ // throw new ...
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java
index f62cf502146..96bc1893bbf 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptorCache.java
@@ -20,6 +20,7 @@
import org.cryptomator.cryptolib.api.AuthenticationFailedException;
import org.cryptomator.cryptolib.api.FileNameCryptor;
+import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Objects;
@@ -29,7 +30,7 @@ public class CryptorCache {
public static final BaseEncoding BASE32 = BaseEncoding.base32();
- private final LRUCache directoryIdCache = LRUCache.build(250);
+ private final LRUCache directoryIdCache = LRUCache.build(250);
private final LRUCache decryptCache = LRUCache.build(5000);
private final LRUCache encryptCache = LRUCache.build(5000);
@@ -39,11 +40,12 @@ public CryptorCache(final FileNameCryptor impl) {
this.impl = impl;
}
- public String hashDirectoryId(final String cleartextDirectoryId) {
- if(!directoryIdCache.contains(cleartextDirectoryId)) {
- directoryIdCache.put(cleartextDirectoryId, impl.hashDirectoryId(cleartextDirectoryId));
+ public String hashDirectoryId(final byte[] cleartextDirectoryId) {
+ final ByteBuffer wrap = ByteBuffer.wrap(cleartextDirectoryId);
+ if(!directoryIdCache.contains(wrap)) {
+ directoryIdCache.put(wrap, impl.hashDirectoryId(cleartextDirectoryId));
}
- return directoryIdCache.get(cleartextDirectoryId);
+ return directoryIdCache.get(wrap);
}
public String encryptFilename(final BaseEncoding encoding, final String cleartextName, final byte[] associatedData) {
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java
index 5276d3e3d03..3b66ca2b21a 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoAttributesFeature.java
@@ -51,7 +51,7 @@ public PathAttributes find(final Path file, final ListProgressListener listener)
if(file.isDirectory()) {
attributes.setSize(-1L);
}
- attributes.setVault(vault.getHome());
+ attributes.setVaultMetadata(vault.getMetadata());
return attributes;
}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java
index 90f7be910c4..2aa4f98ff72 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java
@@ -21,7 +21,7 @@
import ch.cyberduck.core.RandomStringService;
import ch.cyberduck.core.Session;
import ch.cyberduck.core.UUIDRandomStringService;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
import ch.cyberduck.core.exception.BackgroundException;
@@ -45,9 +45,9 @@ public class CryptoBulkFeature implements Bulk {
private final Session> session;
private final Bulk delegate;
- private final CryptoVault cryptomator;
+ private final AbstractVault cryptomator;
- public CryptoBulkFeature(final Session> session, final Bulk delegate, final CryptoVault cryptomator) {
+ public CryptoBulkFeature(final Session> session, final Bulk delegate, final AbstractVault cryptomator) {
this.session = session;
this.delegate = delegate;
this.cryptomator = cryptomator;
@@ -82,9 +82,8 @@ public int compare(final Map.Entry o1, final Map.E
if(!status.isExists()) {
switch(type) {
case upload:
- // Preset directory ID for new folders to avert lookup with not found failure in directory ID provider
- final String directoryId = random.random();
- encrypted.put(new TransferItem(cryptomator.encrypt(session, file, directoryId, false), local), status);
+ cryptomator.getDirectoryProvider().createDirectoryId(file);
+ encrypted.put(new TransferItem(cryptomator.encrypt(session, file, false), local), status);
break;
default:
encrypted.put(new TransferItem(cryptomator.encrypt(session, file), local), status);
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java
index c591b99d69c..41ae969b7fd 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoChecksumCompute.java
@@ -15,8 +15,8 @@
* GNU General Public License for more details.
*/
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.CryptoOutputStream;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
import ch.cyberduck.core.exception.BackgroundException;
@@ -55,11 +55,11 @@
public class CryptoChecksumCompute extends AbstractChecksumCompute {
private static final Logger log = LogManager.getLogger(CryptoChecksumCompute.class);
- private final CryptoVault cryptomator;
+ private final AbstractVault cryptomator;
private final ChecksumCompute delegate;
- public CryptoChecksumCompute(final ChecksumCompute delegate, final CryptoVault vault) {
- this.cryptomator = vault;
+ public CryptoChecksumCompute(final ChecksumCompute delegate, final AbstractVault CryptoVaultInterface) {
+ this.cryptomator = CryptoVaultInterface;
this.delegate = delegate;
}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java
index 83d536d17b2..cc172478978 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java
@@ -19,7 +19,7 @@
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
import ch.cyberduck.core.exception.BackgroundException;
@@ -37,11 +37,11 @@ public class CryptoCopyFeature implements Copy {
private final Session> session;
private final Copy proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
private Session> target;
- public CryptoCopyFeature(final Session> session, final Copy proxy, final CryptoVault vault) {
+ public CryptoCopyFeature(final Session> session, final Copy proxy, final AbstractVault vault) {
this.session = session;
this.target = session;
this.proxy = proxy;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java
deleted file mode 100644
index cbdda42cf13..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package ch.cyberduck.core.cryptomator.features;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.DisabledListProgressListener;
-import ch.cyberduck.core.ListService;
-import ch.cyberduck.core.PasswordCallback;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoFilename;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.AccessDeniedException;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.features.Trash;
-import ch.cyberduck.core.transfer.TransferStatus;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-
-public class CryptoDeleteV6Feature implements Delete, Trash {
- private static final Logger log = LogManager.getLogger(CryptoDeleteV6Feature.class);
-
- private final Session> session;
- private final Delete proxy;
- private final CryptoVault vault;
- private final CryptoFilename filenameProvider;
-
- public CryptoDeleteV6Feature(final Session> session, final Delete proxy, final CryptoVault vault) {
- this.session = session;
- this.proxy = proxy;
- this.vault = vault;
- this.filenameProvider = vault.getFilenameProvider();
- }
-
- @Override
- public void delete(final Map files, final PasswordCallback prompt, final Callback callback) throws BackgroundException {
- for(Path f : files.keySet()) {
- final List metadataFiles = new ArrayList<>();
- if(!f.equals(vault.getHome())) {
- final Path encrypt = vault.encrypt(session, f);
- try {
- proxy.delete(Collections.singletonList(encrypt), prompt, callback);
- }
- catch(NotfoundException | AccessDeniedException e) {
- if(f.isDirectory()) {
- log.error("Failure {} deleting directory {}", e, encrypt);
- }
- else {
- throw e;
- }
- }
- final Path metadata = vault.encrypt(session, f, true);
- if(f.isDirectory()) {
- // Delete metadata file for directory
- log.debug("Add metadata file {}", metadata);
- metadataFiles.add(metadata);
- vault.getDirectoryProvider().delete(f);
- }
- if(filenameProvider.isDeflated(metadata.getName())) {
- filenameProvider.invalidate(filenameProvider.inflate(session, metadata.getName()));
- final Path metadataFile = filenameProvider.resolve(metadata.getName());
- log.debug("Add metadata file {}", metadata);
- metadataFiles.add(metadataFile);
- }
- }
- if(!metadataFiles.isEmpty()) {
- proxy.delete(metadataFiles, prompt, callback);
- }
- }
- for(Path f : files.keySet()) {
- if(f.equals(vault.getHome())) {
- log.warn("Recursively delete vault {}", f);
- final List metadata = new ArrayList<>();
- if(!proxy.features(f).contains(Delete.Flags.recursive)) {
- final Find find = session._getFeature(Find.class);
- final Path dataRoot = new Path(f, "d", f.getType());
- if(find.find(dataRoot)) {
- for(Path d : session._getFeature(ListService.class).list(dataRoot, new DisabledListProgressListener()).toList()) {
- metadata.addAll(session._getFeature(ListService.class).list(d, new DisabledListProgressListener()).toList());
- metadata.add(d);
- }
- metadata.add(dataRoot);
- }
- final Path metaRoot = new Path(f, "m", f.getType());
- if(find.find(metaRoot)) {
- for(Path m : session._getFeature(ListService.class).list(metaRoot, new DisabledListProgressListener()).toList()) {
- for(Path m2 : session._getFeature(ListService.class).list(m, new DisabledListProgressListener()).toList()) {
- metadata.addAll(session._getFeature(ListService.class).list(m2, new DisabledListProgressListener()).toList());
- metadata.add(m2);
- }
- metadata.add(m);
- }
- metadata.add(metaRoot);
- }
- metadata.add(vault.getMasterkey());
- }
- metadata.add(f);
- proxy.delete(metadata, prompt, callback);
- }
- }
- }
-
- @Override
- public void preflight(final Path file) throws BackgroundException {
- proxy.preflight(vault.encrypt(session, file));
- }
-
- @Override
- public EnumSet features(final Path file) {
- return proxy.features(file);
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("CryptoDeleteV6Feature{");
- sb.append("proxy=").append(proxy);
- sb.append('}');
- return sb.toString();
- }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java
index ab1d6b34b73..1c8c02c26bd 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java
@@ -15,14 +15,14 @@
* GNU General Public License for more details.
*/
+import ch.cyberduck.core.AbstractPath;
import ch.cyberduck.core.DisabledListProgressListener;
import ch.cyberduck.core.ListService;
import ch.cyberduck.core.PasswordCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.CryptoFilename;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
@@ -45,10 +45,10 @@ public class CryptoDeleteV7Feature implements Delete, Trash {
private final Session> session;
private final Delete proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
private final CryptoFilename filenameProvider;
- public CryptoDeleteV7Feature(final Session> session, final Delete proxy, final CryptoVault vault) {
+ public CryptoDeleteV7Feature(final Session> session, final Delete proxy, final AbstractVault vault) {
this.session = session;
this.proxy = proxy;
this.vault = vault;
@@ -62,8 +62,8 @@ public void delete(final Map files, final PasswordCallback
if(!f.equals(vault.getHome())) {
final Path encrypt = vault.encrypt(session, f);
if(f.isDirectory()) {
- final Path backup = new Path(encrypt, CryptoDirectoryV7Provider.BACKUP_DIRECTORY_METADATAFILE,
- EnumSet.of(Path.Type.file));
+ final Path backup = new Path(encrypt, vault.getBackupDirectoryMetadataFilename(),
+ EnumSet.of(AbstractPath.Type.file));
try {
log.debug("Deleting directory id backup file {}", backup);
proxy.delete(Collections.singletonList(backup), prompt, callback);
@@ -87,7 +87,7 @@ public void delete(final Map files, final PasswordCallback
}
final Path metadata = vault.encrypt(session, f, true);
if(f.isDirectory()) {
- final Path metadataFile = new Path(metadata, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file));
+ final Path metadataFile = new Path(metadata, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file));
log.debug("Add metadata file {}", metadataFile);
metadataFiles.add(metadataFile);
metadataFiles.add(metadata);
@@ -124,8 +124,8 @@ public void delete(final Map files, final PasswordCallback
}
metadata.add(dataRoot);
}
- if(vault.getMasterkey() != null) {
- metadata.add(vault.getMasterkey());
+ if(vault.getMasterkeyPath() != null) {
+ metadata.add(vault.getMasterkeyPath());
}
if(find.find(vault.getConfig())) {
metadata.add(vault.getConfig());
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryUVFFeature.java
similarity index 52%
rename from cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java
rename to cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryUVFFeature.java
index 1f92916e928..82c920466db 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryUVFFeature.java
@@ -1,7 +1,7 @@
package ch.cyberduck.core.cryptomator.features;
/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
@@ -16,11 +16,9 @@
*/
import ch.cyberduck.core.Path;
-import ch.cyberduck.core.RandomStringService;
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.ContentWriter;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Directory;
@@ -30,65 +28,69 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
import org.cryptomator.cryptolib.api.FileHeader;
-import java.nio.charset.StandardCharsets;
+import java.util.EnumSet;
-public class CryptoDirectoryV6Feature implements Directory {
- private static final Logger log = LogManager.getLogger(CryptoDirectoryV6Feature.class);
+public class CryptoDirectoryUVFFeature extends CryptoDirectoryV7Feature {
+ private static final Logger log = LogManager.getLogger(CryptoDirectoryUVFFeature.class);
private final Session> session;
private final Directory delegate;
- private final CryptoVault vault;
- private final RandomStringService random = new UUIDRandomStringService();
+ private final AbstractVault vault;
- public CryptoDirectoryV6Feature(final Session> session, final Directory delegate, final CryptoVault cryptomator) {
+ public CryptoDirectoryUVFFeature(final Session> session, final Directory delegate, final AbstractVault vault) {
+ super(session, delegate, vault);
this.session = session;
this.delegate = delegate;
- this.vault = cryptomator;
+ this.vault = vault;
}
+ //TODO check if we can merge this implementation with the one in the superclass. Here we additionally write the recovery metadata file.
@Override
public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException {
- final Path encrypt = vault.encrypt(session, folder, random.random(), false);
- final String directoryId = encrypt.attributes().getDirectoryId();
+ final DirectoryMetadata dirMetadata = vault.getDirectoryProvider().createDirectoryId(folder);
// Create metadata file for directory
- final Path directoryMetadataFile = vault.encrypt(session, folder, true);
+ final Path directoryMetadataFolder = session._getFeature(Directory.class).mkdir(
+ session._getFeature(Write.class), vault.encrypt(session, folder, true), new TransferStatus().setRegion(status.getRegion()));
+ final Path directoryMetadataFile = new Path(directoryMetadataFolder,
+ vault.getDirectoryMetadataFilename(),
+ EnumSet.of(Path.Type.file));
log.debug("Write metadata {} for folder {}", directoryMetadataFile, folder);
- new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8));
+ final byte[] encryptedMetadata = this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata);
+ new ContentWriter(session).write(directoryMetadataFile, encryptedMetadata);
+ final Path encrypt = vault.encrypt(session, folder, false);
final Path intermediate = encrypt.getParent();
if(!session._getFeature(Find.class).find(intermediate)) {
- session._getFeature(Directory.class).mkdir(session._getFeature(Write.class), intermediate, new TransferStatus().setRegion(status.getRegion()));
+ session._getFeature(Directory.class).mkdir(
+ session._getFeature(Write.class), intermediate, new TransferStatus().setRegion(status.getRegion()));
}
- // Write header
+
+ // Write metadata
final FileHeader header = vault.getFileHeaderCryptor().create();
status.setHeader(vault.getFileHeaderCryptor().encryptHeader(header));
status.setNonces(new RandomNonceGenerator(vault.getNonceSize()));
final Path target = delegate.mkdir(writer, encrypt, status);
+ final Path recoveryDirectoryMetadataFile = new Path(target,
+ vault.getDirectoryMetadataFilename(),
+ EnumSet.of(Path.Type.file));
+ log.debug("Write recovery metadata {} for folder {}", recoveryDirectoryMetadataFile, folder);
+ new ContentWriter(session).write(recoveryDirectoryMetadataFile, this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata));
// Implementation may return new copy of attributes without encryption attributes
- target.attributes().setDirectoryId(directoryId);
+ target.attributes().setDirectoryId(encryptedMetadata);
target.attributes().setDecrypted(folder);
// Make reference of encrypted path in attributes of decrypted file point to metadata file
final Path decrypt = vault.decrypt(session, vault.encrypt(session, target, true));
- decrypt.attributes().setFileId(directoryMetadataFile.attributes().getFileId());
- decrypt.attributes().setVersionId(directoryMetadataFile.attributes().getVersionId());
+ decrypt.attributes().setFileId(directoryMetadataFolder.attributes().getFileId());
+ decrypt.attributes().setVersionId(directoryMetadataFolder.attributes().getVersionId());
return decrypt;
}
- @Override
- public boolean isSupported(final Path workdir, final String name) {
- return delegate.isSupported(workdir, name);
- }
-
- @Override
- public void preflight(final Path workdir, final String filename) throws BackgroundException {
- delegate.preflight(vault.encrypt(session, workdir), filename);
- }
-
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder("CryptoDirectoryFeature{");
- sb.append("proxy=").append(delegate);
+ final StringBuilder sb = new StringBuilder("CryptoDirectoryUVFFeature{");
+ sb.append("vault=").append(vault);
sb.append('}');
return sb.toString();
}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java
index b32f8eb7733..20ed3259674 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV7Feature.java
@@ -16,12 +16,9 @@
*/
import ch.cyberduck.core.Path;
-import ch.cyberduck.core.RandomStringService;
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.ContentWriter;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Directory;
@@ -31,9 +28,9 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
import org.cryptomator.cryptolib.api.FileHeader;
-import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
public class CryptoDirectoryV7Feature implements Directory {
@@ -41,28 +38,28 @@ public class CryptoDirectoryV7Feature implements Directory {
private final Session> session;
private final Directory delegate;
- private final CryptoVault vault;
- private final RandomStringService random = new UUIDRandomStringService();
+ private final AbstractVault vault;
- public CryptoDirectoryV7Feature(final Session> session, final Directory delegate, final CryptoVault cryptomator) {
+ public CryptoDirectoryV7Feature(final Session> session, final Directory delegate, final AbstractVault vault) {
this.session = session;
this.delegate = delegate;
- this.vault = cryptomator;
+ this.vault = vault;
}
@Override
public Path mkdir(final Write writer, final Path folder, final TransferStatus status) throws BackgroundException {
- final Path encrypt = vault.encrypt(session, folder, random.random(), false);
- final String directoryId = encrypt.attributes().getDirectoryId();
+ final DirectoryMetadata dirMetadata = vault.getDirectoryProvider().createDirectoryId(folder);
// Create metadata file for directory
final Path directoryMetadataFolder = session._getFeature(Directory.class).mkdir(
session._getFeature(Write.class), vault.encrypt(session, folder, true),
new TransferStatus().setRegion(status.getRegion()));
final Path directoryMetadataFile = new Path(directoryMetadataFolder,
- CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE,
+ vault.getDirectoryMetadataFilename(),
EnumSet.of(Path.Type.file));
log.debug("Write metadata {} for folder {}", directoryMetadataFile, folder);
- new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8));
+ final byte[] encryptedMetadata = this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata);
+ new ContentWriter(session).write(directoryMetadataFile, encryptedMetadata);
+ final Path encrypt = vault.encrypt(session, folder, false);
final Path intermediate = encrypt.getParent();
if(!session._getFeature(Find.class).find(intermediate)) {
session._getFeature(Directory.class).mkdir(session._getFeature(Write.class), intermediate, new TransferStatus().setRegion(status.getRegion()));
@@ -73,7 +70,7 @@ public Path mkdir(final Write writer, final Path folder, final TransferSt
status.setNonces(new RandomNonceGenerator(vault.getNonceSize()));
final Path target = delegate.mkdir(writer, encrypt, status);
// Implementation may return new copy of attributes without encryption attributes
- target.attributes().setDirectoryId(directoryId);
+ target.attributes().setDirectoryId(encryptedMetadata);
target.attributes().setDecrypted(folder);
// Make reference of encrypted path in attributes of decrypted file point to metadata file
final Path decrypt = vault.decrypt(session, vault.encrypt(session, target, true));
@@ -94,7 +91,7 @@ public void preflight(final Path workdir, final String filename) throws Backgrou
@Override
public String toString() {
- final StringBuilder sb = new StringBuilder("CryptoDirectoryFeature{");
+ final StringBuilder sb = new StringBuilder("CryptoDirectoryV7Feature{");
sb.append("proxy=").append(delegate);
sb.append('}');
return sb.toString();
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java
index e3dceec4da6..ea0ab365a68 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDownloadFeature.java
@@ -19,12 +19,11 @@
import ch.cyberduck.core.Local;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Download;
import ch.cyberduck.core.features.Read;
-import ch.cyberduck.core.features.Vault;
import ch.cyberduck.core.io.BandwidthThrottle;
import ch.cyberduck.core.io.StreamListener;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -33,9 +32,9 @@ public class CryptoDownloadFeature implements Download {
private final Session> session;
private final Download proxy;
- private final Vault vault;
+ private final AbstractVault vault;
- public CryptoDownloadFeature(final Session> session, final Download proxy, final CryptoVault vault) {
+ public CryptoDownloadFeature(final Session> session, final Download proxy, final AbstractVault vault) {
this.session = session;
this.proxy = proxy;
this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java
index 9f30a97b1be..6dc07f6edf2 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoEncryptionFeature.java
@@ -18,7 +18,6 @@
import ch.cyberduck.core.LoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Encryption;
import ch.cyberduck.core.features.Vault;
@@ -31,7 +30,7 @@ public class CryptoEncryptionFeature implements Encryption {
private final Encryption delegate;
private final Vault vault;
- public CryptoEncryptionFeature(final Session> session, final Encryption delegate, final CryptoVault vault) {
+ public CryptoEncryptionFeature(final Session> session, final Encryption delegate, final Vault vault) {
this.session = session;
this.delegate = delegate;
this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java
deleted file mode 100644
index e43afc0965d..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package ch.cyberduck.core.cryptomator.features;
-
-/*
- * Copyright (c) 2002-2017 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.ConnectionCallback;
-import ch.cyberduck.core.LocaleFactory;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.InvalidFilenameException;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Move;
-import ch.cyberduck.core.transfer.TransferStatus;
-
-import java.text.MessageFormat;
-import java.util.EnumSet;
-import java.util.Optional;
-
-public class CryptoMoveV6Feature implements Move {
-
- private final Session> session;
- private final Move proxy;
- private final CryptoVault vault;
-
- public CryptoMoveV6Feature(final Session> session, final Move delegate, final CryptoVault cryptomator) {
- this.session = session;
- this.proxy = delegate;
- this.vault = cryptomator;
- }
-
- @Override
- public Path move(final Path file, final Path renamed, final TransferStatus status, final Delete.Callback callback, final ConnectionCallback connectionCallback) throws BackgroundException {
- // Move inside vault moves actual files and only metadata files for directories but not the actual directories
- final Path target = proxy.move(
- vault.encrypt(session, file, file.isDirectory()),
- vault.encrypt(session, renamed, file.isDirectory()), status, callback, connectionCallback);
- if(file.isDirectory()) {
- vault.getDirectoryProvider().delete(file);
- }
- if(vault.contains(target)) {
- return vault.decrypt(session, target);
- }
- return target;
- }
-
- @Override
- public EnumSet features(final Path source, final Path target) {
- // No need to handle recursion with encrypted filenames
- return EnumSet.of(Flags.recursive);
- }
-
- @Override
- public void preflight(final Path source, final Optional target) throws BackgroundException {
- if(target.isPresent()) {
- if(!vault.getFilenameProvider().isValid(target.get().getName())) {
- throw new InvalidFilenameException(MessageFormat.format(LocaleFactory.localizedString("Cannot create {0}", "Error"), target.get().getName())).withFile(source);
- }
- proxy.preflight(vault.encrypt(session, source, source.isDirectory()), Optional.of(vault.encrypt(session, target.get(), source.isDirectory())));
- }
- else {
- proxy.preflight(vault.encrypt(session, source, source.isDirectory()), target);
- }
- }
-
- @Override
- public Move withTarget(final Session> session) {
- proxy.withTarget(session);
- return this;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("CryptoMoveFeature{");
- sb.append("proxy=").append(proxy);
- sb.append('}');
- return sb.toString();
- }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java
index a0bd1c8eb7f..ea1c2fd5659 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV7Feature.java
@@ -19,8 +19,7 @@
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.InvalidFilenameException;
import ch.cyberduck.core.features.Delete;
@@ -35,12 +34,12 @@ public class CryptoMoveV7Feature implements Move {
private final Session> session;
private final Move proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
- public CryptoMoveV7Feature(final Session> session, final Move delegate, final CryptoVault cryptomator) {
+ public CryptoMoveV7Feature(final Session> session, final Move delegate, final AbstractVault vault) {
this.session = session;
this.proxy = delegate;
- this.vault = cryptomator;
+ this.vault = vault;
}
@Override
@@ -51,8 +50,8 @@ public Path move(final Path file, final Path renamed, final TransferStatus statu
final Path target = proxy.move(sourceEncrypted, targetEncrypted, status, callback, connectionCallback);
if(file.isDirectory()) {
if(!proxy.isRecursive(file, renamed)) {
- proxy.move(new Path(sourceEncrypted, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file)),
- new Path(targetEncrypted, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file)),
+ proxy.move(new Path(sourceEncrypted, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file)),
+ new Path(targetEncrypted, vault.getBackupDirectoryMetadataFilename(), EnumSet.of(Path.Type.file)),
new TransferStatus(status), callback, connectionCallback);
}
vault.getDirectoryProvider().delete(file);
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java
index 4943f838a28..f5d6de84717 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java
@@ -16,18 +16,18 @@
*/
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.features.Find;
import ch.cyberduck.core.features.MultipartWrite;
import ch.cyberduck.core.features.Write;
public class CryptoMultipartWriteFeature extends CryptoWriteFeature implements MultipartWrite {
- public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final CryptoVault vault) {
+ public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final AbstractVault vault) {
super(session, delegate, vault);
}
- public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final Find finder, final AttributesFinder attributes, final CryptoVault vault) {
+ public CryptoMultipartWriteFeature(final Session> session, final Write delegate, final Find finder, final AttributesFinder attributes, final AbstractVault vault) {
super(session, delegate, vault);
}
}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java
index ec8286a4624..45e499a7ed2 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java
@@ -19,8 +19,8 @@
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.CryptoInputStream;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Read;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -36,9 +36,9 @@ public class CryptoReadFeature implements Read {
private final Session> session;
private final Read proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
- public CryptoReadFeature(final Session> session, final Read proxy, final CryptoVault vault) {
+ public CryptoReadFeature(final Session> session, final Read proxy, final AbstractVault vault) {
this.session = session;
this.proxy = proxy;
this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java
index 9db63f0ae3d..437a3c0697e 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java
@@ -17,8 +17,8 @@
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.CryptoTransferStatus;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Timestamp;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -27,9 +27,9 @@ public class CryptoTimestampFeature implements Timestamp {
private final Session> session;
private final Timestamp proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
- public CryptoTimestampFeature(final Session> session, final Timestamp proxy, final CryptoVault vault) {
+ public CryptoTimestampFeature(final Session> session, final Timestamp proxy, final AbstractVault vault) {
this.session = session;
this.proxy = proxy;
this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java
index e8864566a94..7a0493e49d3 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java
@@ -19,7 +19,7 @@
import ch.cyberduck.core.Path;
import ch.cyberduck.core.PathAttributes;
import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.InvalidFilenameException;
@@ -35,9 +35,9 @@ public class CryptoTouchFeature implements Touch {
private final Session> session;
private final Touch proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
- public CryptoTouchFeature(final Session> session, final Touch proxy, final CryptoVault cryptomator) {
+ public CryptoTouchFeature(final Session> session, final Touch proxy, final AbstractVault cryptomator) {
this.session = session;
this.proxy = proxy;
this.vault = cryptomator;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java
index 2f2c1317338..4d65ed8745d 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoUploadFeature.java
@@ -20,8 +20,8 @@
import ch.cyberduck.core.Path;
import ch.cyberduck.core.ProgressListener;
import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.CryptoTransferStatus;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.features.Upload;
import ch.cyberduck.core.features.Write;
@@ -33,9 +33,9 @@ public class CryptoUploadFeature implements Upload {
private final Session> session;
private final Upload proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
- public CryptoUploadFeature(final Session> session, final Upload delegate, final CryptoVault vault) {
+ public CryptoUploadFeature(final Session> session, final Upload delegate, final AbstractVault vault) {
this.session = session;
this.proxy = delegate;
this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java
index 7a8ea6f211b..7142146e0e1 100644
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoWriteFeature.java
@@ -19,9 +19,9 @@
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Session;
+import ch.cyberduck.core.cryptomator.AbstractVault;
import ch.cyberduck.core.cryptomator.CryptoOutputStream;
import ch.cyberduck.core.cryptomator.CryptoTransferStatus;
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator;
import ch.cyberduck.core.exception.BackgroundException;
@@ -42,9 +42,9 @@ public class CryptoWriteFeature implements Write {
private final Session> session;
private final Write proxy;
- private final CryptoVault vault;
+ private final AbstractVault vault;
- public CryptoWriteFeature(final Session> session, final Write proxy, final CryptoVault vault) {
+ public CryptoWriteFeature(final Session> session, final Write proxy, final AbstractVault vault) {
this.session = session;
this.proxy = proxy;
this.vault = vault;
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java
new file mode 100644
index 00000000000..4446018523e
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java
@@ -0,0 +1,77 @@
+package ch.cyberduck.core.cryptomator.impl;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.ContentReader;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.NotfoundException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+
+import java.util.EnumSet;
+
+public class CryptoDirectoryUVFProvider extends CryptoDirectoryV8Provider {
+ private static final Logger log = LogManager.getLogger(CryptoDirectoryUVFProvider.class);
+
+ private final Path home;
+ private final AbstractVault vault;
+ private final CryptoFilename filenameProvider;
+
+ public CryptoDirectoryUVFProvider(final AbstractVault vault, final CryptoFilename filenameProvider) {
+ super(vault, filenameProvider);
+ this.filenameProvider = filenameProvider;
+ this.home = vault.getHome();
+ this.vault = vault;
+ }
+
+ //TODO kann das auch ersetzt werden mit der impl der superklasse? hier wird load verwendet? nötig?
+ @Override
+ public String toEncrypted(final Session> session, final Path parent, final String filename, final EnumSet type) throws BackgroundException {
+ final DirectoryMetadata dirMetadata = load(session, parent);
+ this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename);
+ final String ciphertextName = this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename);
+ log.debug("Encrypted filename {} to {}", filename, ciphertextName);
+ return filenameProvider.deflate(session, ciphertextName);
+ }
+
+ protected DirectoryMetadata load(final Session> session, final Path directory) throws BackgroundException {
+ if(new SimplePathPredicate(home).test(directory)) {
+ return vault.getRootDirId();
+ }
+ final Path parent = this.toEncrypted(session, directory.getParent());
+ final String cleartextName = directory.getName();
+ final String ciphertextName = this.toEncrypted(session, directory.getParent(), cleartextName, EnumSet.of(Path.Type.directory));
+ final Path metadataParent = new Path(parent, ciphertextName, EnumSet.of(Path.Type.directory));
+ // Read directory id from file
+ try {
+ log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
+ final Path metadataFile = new Path(metadataParent, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file, Path.Type.encrypted));
+ final byte[] ciphertext = new ContentReader(session).readBytes(metadataFile);
+ return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(ciphertext);
+ }
+ catch(NotfoundException e) {
+ log.warn("Missing directory ID for folder {}", directory);
+ return this.getOrCreateDirectoryId(session, directory);
+ }
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java
deleted file mode 100644
index 13b1323fb98..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.CacheReference;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.PathAttributes;
-import ch.cyberduck.core.RandomStringService;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.SimplePathPredicate;
-import ch.cyberduck.core.UUIDRandomStringService;
-import ch.cyberduck.core.cache.LRUCache;
-import ch.cyberduck.core.cryptomator.ContentReader;
-import ch.cyberduck.core.cryptomator.CryptoDirectory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.cryptomator.CryptorCache;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.preferences.PreferencesFactory;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.nio.charset.StandardCharsets;
-import java.util.EnumSet;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-public class CryptoDirectoryV6Provider implements CryptoDirectory {
- private static final Logger log = LogManager.getLogger(CryptoDirectoryV6Provider.class);
-
- private static final String DATA_DIR_NAME = "d";
- private static final String ROOT_DIR_ID = StringUtils.EMPTY;
-
- private final Path dataRoot;
- private final Path home;
- private final CryptoVault cryptomator;
-
- private final RandomStringService random
- = new UUIDRandomStringService();
-
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- private final LRUCache, String> cache = LRUCache.build(
- PreferencesFactory.get().getInteger("cryptomator.cache.size"));
-
- public CryptoDirectoryV6Provider(final Path vault, final CryptoVault cryptomator) {
- this.home = vault;
- this.dataRoot = new Path(vault, DATA_DIR_NAME, vault.getType());
- this.cryptomator = cryptomator;
- }
-
- @Override
- public String toEncrypted(final Session> session, final String directoryId, final String filename, final EnumSet type) throws BackgroundException {
- final String prefix = type.contains(Path.Type.directory) ? CryptoVault.DIR_PREFIX : "";
- final String ciphertextName = prefix + cryptomator.getFileNameCryptor().encryptFilename(CryptorCache.BASE32, filename, directoryId.getBytes(StandardCharsets.UTF_8));
- log.debug("Encrypted filename {} to {}", filename, ciphertextName);
- return cryptomator.getFilenameProvider().deflate(session, ciphertextName);
- }
-
- @Override
- public Path toEncrypted(final Session> session, final String directoryId, final Path directory) throws BackgroundException {
- if(!directory.isDirectory()) {
- throw new NotfoundException(directory.getAbsolute());
- }
- if(new SimplePathPredicate(directory).test(home) || directory.isChild(home)) {
- final PathAttributes attributes = new PathAttributes(directory.attributes());
- // The root of the vault is a different target directory and file ids always correspond to the metadata file
- attributes.setVersionId(null);
- attributes.setFileId(null);
- // Remember random directory id for use in vault
- final String id = this.toDirectoryId(session, directory, directoryId);
- log.debug("Use directory ID '{}' for folder {}", id, directory);
- attributes.setDirectoryId(id);
- attributes.setDecrypted(directory);
- final String directoryIdHash = cryptomator.getFileNameCryptor().hashDirectoryId(id);
- // Intermediate directory
- final Path intermediate = new Path(dataRoot, directoryIdHash.substring(0, 2), dataRoot.getType());
- // Add encrypted type
- final EnumSet type = EnumSet.copyOf(directory.getType());
- type.add(Path.Type.encrypted);
- type.remove(Path.Type.decrypted);
- return new Path(intermediate, directoryIdHash.substring(2), type, attributes);
- }
- throw new NotfoundException(directory.getAbsolute());
- }
-
- private String toDirectoryId(final Session> session, final Path directory, final String directoryId) throws BackgroundException {
- if(new SimplePathPredicate(home).test(directory)) {
- return ROOT_DIR_ID;
- }
- try {
- lock.readLock().lock();
- if(cache.contains(new SimplePathPredicate(directory))) {
- final String existing = cache.get(new SimplePathPredicate(directory));
- if(StringUtils.isNotBlank(directoryId)) {
- if(!existing.equals(directoryId)) {
- log.warn("Do not override already cached id {} with {}", existing, directoryId);
- }
- }
- return existing;
- }
- }
- finally {
- lock.readLock().unlock();
- }
- try {
- log.debug("Acquire lock for {}", directory);
- lock.writeLock().lock();
- final String id = StringUtils.isBlank(directoryId) ? this.load(session, directory) : directoryId;
- cache.put(new SimplePathPredicate(directory), id);
- return id;
- }
- finally {
- lock.writeLock().unlock();
- }
- }
-
- protected String load(final Session> session, final Path directory) throws BackgroundException {
- final Path parent = this.toEncrypted(session, directory.getParent().attributes().getDirectoryId(), directory.getParent());
- final String cleartextName = directory.getName();
- final String ciphertextName = this.toEncrypted(session, parent.attributes().getDirectoryId(), cleartextName, EnumSet.of(Path.Type.directory));
- // Read directory id from file
- try {
- log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
- final Path metadataFile = new Path(parent, ciphertextName, EnumSet.of(Path.Type.file, Path.Type.encrypted));
- return new ContentReader(session).read(metadataFile);
- }
- catch(NotfoundException e) {
- log.warn("Missing directory ID for folder {}", directory);
- return random.random();
- }
- }
-
- public void delete(final Path directory) {
- try {
- lock.writeLock().lock();
- cache.remove(new SimplePathPredicate(directory));
- }
- finally {
- lock.writeLock().unlock();
- }
- }
-
- @Override
- public void destroy() {
- try {
- lock.writeLock().lock();
- cache.clear();
- }
- finally {
- lock.writeLock().unlock();
- }
- }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java
deleted file mode 100644
index 28b4bb6211d..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.RandomStringService;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.UUIDRandomStringService;
-import ch.cyberduck.core.cryptomator.ContentReader;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.nio.charset.StandardCharsets;
-import java.util.EnumSet;
-
-import com.google.common.io.BaseEncoding;
-
-public class CryptoDirectoryV7Provider extends CryptoDirectoryV6Provider {
- private static final Logger log = LogManager.getLogger(CryptoDirectoryV7Provider.class);
-
- public static final String EXTENSION_REGULAR = ".c9r";
- public static final String FILENAME_DIRECTORYID = "dir";
- public static final String DIRECTORY_METADATAFILE = String.format("%s%s", FILENAME_DIRECTORYID, EXTENSION_REGULAR);
- public static final String BACKUP_FILENAME_DIRECTORYID = "dirid";
- public static final String BACKUP_DIRECTORY_METADATAFILE = String.format("%s%s", BACKUP_FILENAME_DIRECTORYID, EXTENSION_REGULAR);
-
- private final CryptoVault cryptomator;
-
- private final RandomStringService random
- = new UUIDRandomStringService();
-
- public CryptoDirectoryV7Provider(final Path vault, final CryptoVault cryptomator) {
- super(vault, cryptomator);
- this.cryptomator = cryptomator;
- }
-
- @Override
- public String toEncrypted(final Session> session, final String directoryId, final String filename, final EnumSet type) throws BackgroundException {
- final String ciphertextName = cryptomator.getFileNameCryptor().encryptFilename(BaseEncoding.base64Url(),
- filename, directoryId.getBytes(StandardCharsets.UTF_8)) + EXTENSION_REGULAR;
- log.debug("Encrypted filename {} to {}", filename, ciphertextName);
- return cryptomator.getFilenameProvider().deflate(session, ciphertextName);
- }
-
- protected String load(final Session> session, final Path directory) throws BackgroundException {
- final Path parent = this.toEncrypted(session, directory.getParent().attributes().getDirectoryId(), directory.getParent());
- final String cleartextName = directory.getName();
- final String ciphertextName = this.toEncrypted(session, parent.attributes().getDirectoryId(), cleartextName, EnumSet.of(Path.Type.directory));
- final Path metadataParent = new Path(parent, ciphertextName, EnumSet.of(Path.Type.directory));
- // Read directory id from file
- try {
- log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
- final Path metadataFile = new Path(metadataParent, CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file, Path.Type.encrypted));
- return new ContentReader(session).read(metadataFile);
- }
- catch(NotfoundException e) {
- log.warn("Missing directory ID for folder {}", directory);
- return random.random();
- }
- }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8Provider.java
new file mode 100644
index 00000000000..04faac5007b
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8Provider.java
@@ -0,0 +1,195 @@
+package ch.cyberduck.core.cryptomator.impl;
+
+/*
+ * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.AbstractPath;
+import ch.cyberduck.core.CacheReference;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.PathAttributes;
+import ch.cyberduck.core.RandomStringService;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cache.LRUCache;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.ContentReader;
+import ch.cyberduck.core.cryptomator.CryptoDirectory;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.NotfoundException;
+import ch.cyberduck.core.preferences.PreferencesFactory;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+
+import java.util.EnumSet;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class CryptoDirectoryV8Provider implements CryptoDirectory {
+ private static final Logger log = LogManager.getLogger(CryptoDirectoryV8Provider.class);
+
+ private static final String DATA_DIR_NAME = "d";
+
+ private final AbstractVault vault;
+ private final Path dataRoot;
+ private final Path home;
+ private final CryptoFilename filenameProvider;
+
+ private final RandomStringService random
+ = new UUIDRandomStringService();
+
+ protected final ReadWriteLock lock = new ReentrantReadWriteLock();
+ protected final LRUCache, byte[]> cache = LRUCache.build(
+ PreferencesFactory.get().getInteger("cryptomator.cache.size"));
+
+
+ public CryptoDirectoryV8Provider(final AbstractVault vault, final CryptoFilename filenameProvider) {
+ this.home = vault.getHome();
+ this.dataRoot = new Path(vault.getHome(), DATA_DIR_NAME, vault.getHome().getType());
+ this.filenameProvider = filenameProvider;
+ this.vault = vault;
+ }
+
+ @Override
+ public String toEncrypted(final Session> session, final Path parent, final String filename, final EnumSet type) throws BackgroundException {
+ final DirectoryMetadata dirMetadata = this.getOrCreateDirectoryId(session, parent);
+ this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename);
+ final String ciphertextName = this.vault.getCryptor().directoryContentCryptor().fileNameEncryptor(dirMetadata).encrypt(filename);
+ log.debug("Encrypted filename {} to {}", filename, ciphertextName);
+ return filenameProvider.deflate(session, ciphertextName);
+ }
+
+ @Override
+ public Path toEncrypted(final Session> session, final Path directory) throws BackgroundException {
+ if(!directory.isDirectory()) {
+ throw new NotfoundException(directory.getAbsolute());
+ }
+ if(new SimplePathPredicate(directory).test(home) || directory.isChild(home)) {
+ final PathAttributes attributes = new PathAttributes(directory.attributes());
+ // The root of the vault is a different target directory and file ids always correspond to the metadata file
+ attributes.setVersionId(null);
+ attributes.setFileId(null);
+ // Remember random directory metadata for use in vault
+ final DirectoryMetadata dirMetadata = this.getOrCreateDirectoryId(session, directory);
+ log.debug("Use directory ID '{}' for folder {}", dirMetadata, directory);
+ attributes.setDirectoryId(this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(dirMetadata));
+ attributes.setDecrypted(directory);
+ final String dirPath = this.vault.getCryptor().directoryContentCryptor().dirPath(dirMetadata);
+ final String[] segments = StringUtils.split(dirPath, '/');
+ // Intermediate directory
+ final Path intermediate = new Path(dataRoot, segments[1], dataRoot.getType());
+ // Add encrypted type
+ final EnumSet type = EnumSet.copyOf(directory.getType());
+ type.add(Path.Type.encrypted);
+ type.remove(Path.Type.decrypted);
+ return new Path(intermediate, segments[2], type, attributes);
+ }
+ throw new NotfoundException(directory.getAbsolute());
+ }
+
+ protected DirectoryMetadata toDirectoryId(final Session> session, final Path directory) throws BackgroundException {
+ if(new SimplePathPredicate(home).test(directory)) {
+ return this.vault.getRootDirId();
+ }
+ lock.readLock().lock();
+ try {
+ if(cache.contains(new SimplePathPredicate(directory))) {
+ return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(cache.get(new SimplePathPredicate(directory)));
+ }
+ }
+ finally {
+ lock.readLock().unlock();
+ }
+ try {
+ log.debug("Acquire lock for {}", directory);
+ lock.writeLock().lock();
+ final DirectoryMetadata id = this.load(session, directory);
+ cache.put(new SimplePathPredicate(directory), this.vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(id));
+ return id;
+ }
+ finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ public void delete(final Path directory) {
+ lock.writeLock().lock();
+ try {
+ cache.remove(new SimplePathPredicate(directory));
+ }
+ finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public void destroy() {
+ lock.writeLock().lock();
+ try {
+ cache.clear();
+ }
+ finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public DirectoryMetadata getOrCreateDirectoryId(final Session> session, final Path file) throws BackgroundException {
+ if(file.attributes().getDirectoryId() != null) {
+ return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(file.attributes().getDirectoryId());
+ }
+ final Path decrypted = file.getType().contains(AbstractPath.Type.encrypted) ? file.attributes().getDecrypted() : file;
+ return this.toDirectoryId(session, decrypted.getType().contains(AbstractPath.Type.file) ? decrypted.getParent() : decrypted);
+ }
+
+ @Override
+ public DirectoryMetadata createDirectoryId(final Path directory) {
+ lock.writeLock().lock();
+ try {
+ final DirectoryMetadata metadata = vault.getCryptor().directoryContentCryptor().newDirectoryMetadata();
+ final byte[] encrypted = vault.getCryptor().directoryContentCryptor().encryptDirectoryMetadata(metadata);
+ cache.put(new SimplePathPredicate(directory), encrypted);
+ return metadata;
+ }
+ finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ protected DirectoryMetadata load(final Session> session, final Path directory) throws BackgroundException {
+ final Path encryptedParent = this.toEncrypted(session, directory.getParent());
+ final String cleartextName = directory.getName();
+ final String ciphertextName = this.toEncrypted(session, directory.getParent(), cleartextName, EnumSet.of(Path.Type.directory));
+ final Path metadataParent = new Path(encryptedParent, ciphertextName, EnumSet.of(Path.Type.directory));
+ // Read directory id from file
+ try {
+ log.debug("Read directory ID for folder {} from {}", directory, ciphertextName);
+ final Path metadataFile = new Path(metadataParent, vault.getDirectoryMetadataFilename(), EnumSet.of(Path.Type.file, Path.Type.encrypted));
+ final byte[] bytes = new ContentReader(session).readBytes(metadataFile);
+ return this.vault.getCryptor().directoryContentCryptor().decryptDirectoryMetadata(bytes);
+ }
+ catch(NotfoundException e) {
+ log.warn("Missing directory ID for folder {}", directory);
+ return this.createDirectoryId(directory);
+ //TODO check if we need to propagtae exception instead?
+ // throw e;
+
+ }
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java
deleted file mode 100644
index 6cb8efa9160..00000000000
--- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoFilenameV6Provider.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.Session;
-import ch.cyberduck.core.cache.LRUCache;
-import ch.cyberduck.core.cryptomator.ContentReader;
-import ch.cyberduck.core.cryptomator.ContentWriter;
-import ch.cyberduck.core.cryptomator.CryptoFilename;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.features.Directory;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.features.Write;
-import ch.cyberduck.core.preferences.PreferencesFactory;
-import ch.cyberduck.core.transfer.TransferStatus;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.cryptomator.cryptolib.common.MessageDigestSupplier;
-
-import java.util.EnumSet;
-
-import com.google.common.io.BaseEncoding;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-public class CryptoFilenameV6Provider implements CryptoFilename {
- private static final Logger log = LogManager.getLogger(CryptoFilenameV6Provider.class);
-
- private static final BaseEncoding BASE32 = BaseEncoding.base32();
- private static final String LONG_NAME_FILE_EXT = ".lng";
- private static final String METADATA_DIR_NAME = "m";
-
- public static final int DEFAULT_NAME_SHORTENING_THRESHOLD = 130;
-
- private final int shorteningThreshold;
- private final Path metadataRoot;
-
- private final LRUCache cache = LRUCache.build(
- PreferencesFactory.get().getLong("cryptomator.cache.size"));
-
- public CryptoFilenameV6Provider(final Path vault) {
- this(vault, DEFAULT_NAME_SHORTENING_THRESHOLD);
- }
-
- public CryptoFilenameV6Provider(final Path vault, final int shorteningThreshold) {
- this.metadataRoot = new Path(vault, METADATA_DIR_NAME, vault.getType());
- this.shorteningThreshold = shorteningThreshold;
- }
-
- @Override
- public boolean isDeflated(final String filename) {
- return filename.endsWith(LONG_NAME_FILE_EXT);
- }
-
- @Override
- public boolean isValid(final String filename) {
- return true;
- }
-
- @Override
- public String inflate(final Session> session, final String shortName) throws BackgroundException {
- return new ContentReader(session).read(this.resolve(shortName));
- }
-
- @Override
- public String deflate(final Session> session, final String filename) throws BackgroundException {
- if(filename.length() < shorteningThreshold) {
- return filename;
- }
- if(cache.contains(filename)) {
- return cache.get(filename);
- }
- final byte[] longFileNameBytes = filename.getBytes(UTF_8);
- final byte[] hash = MessageDigestSupplier.SHA1.get().digest(longFileNameBytes);
- final String shortName = BASE32.encode(hash) + LONG_NAME_FILE_EXT;
- final Path metadataFile = this.resolve(shortName);
- final Path secondLevel = metadataFile.getParent();
- final Path firstLevel = secondLevel.getParent();
- final Directory mkdir = session._getFeature(Directory.class);
- final Find find = session._getFeature(Find.class);
- if(!find.find(metadataRoot)) {
- mkdir.mkdir(session._getFeature(Write.class), metadataRoot, new TransferStatus());
- }
- if(!find.find(firstLevel)) {
- mkdir.mkdir(session._getFeature(Write.class), firstLevel, new TransferStatus());
- }
- if(!find.find(secondLevel)) {
- mkdir.mkdir(session._getFeature(Write.class), secondLevel, new TransferStatus());
- }
- if(!find.find(metadataFile)) {
- new ContentWriter(session).write(metadataFile, longFileNameBytes);
- }
- log.info("Deflated {} to {}", filename, shortName);
- cache.put(filename, shortName);
- return shortName;
- }
-
- @Override
- public Path resolve(final String filename) {
- // Intermediate directory
- final Path first = new Path(metadataRoot, filename.substring(0, 2), metadataRoot.getType());
- // Intermediate directory
- final Path second = new Path(first, filename.substring(2, 4), metadataRoot.getType());
- return new Path(second, filename, EnumSet.of(Path.Type.file, Path.Type.encrypted, Path.Type.vault));
- }
-
- @Override
- public void invalidate(final String filename) {
- cache.remove(filename);
- }
-
- @Override
- public void destroy() {
- cache.clear();
- }
-}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/CryptoVault.java
new file mode 100644
index 00000000000..f47c6842e78
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/CryptoVault.java
@@ -0,0 +1,331 @@
+package ch.cyberduck.core.cryptomator.impl.uvf;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.LoginOptions;
+import ch.cyberduck.core.PasswordCallback;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.PathAttributes;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.ContentWriter;
+import ch.cyberduck.core.cryptomator.CryptoDirectory;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.cryptomator.features.CryptoDirectoryUVFFeature;
+import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryUVFProvider;
+import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
+import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.UnsupportedException;
+import ch.cyberduck.core.features.Directory;
+import ch.cyberduck.core.features.Encryption;
+import ch.cyberduck.core.features.Vault;
+import ch.cyberduck.core.features.Write;
+import ch.cyberduck.core.preferences.PreferencesFactory;
+import ch.cyberduck.core.transfer.TransferStatus;
+import ch.cyberduck.core.vault.JWKCallback;
+import ch.cyberduck.core.vault.VaultException;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultMetadataProvider;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.CryptorProvider;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+import org.cryptomator.cryptolib.api.FileContentCryptor;
+import org.cryptomator.cryptolib.api.FileHeaderCryptor;
+import org.cryptomator.cryptolib.api.RevolvingMasterkey;
+import org.cryptomator.cryptolib.api.UVFMasterkey;
+
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+import com.google.auto.service.AutoService;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEObjectJSON;
+import com.nimbusds.jose.crypto.MultiDecrypter;
+
+@AutoService(Vault.class)
+public class CryptoVault extends AbstractVault {
+ private static final Logger log = LogManager.getLogger(CryptoVault.class);
+
+ private static final String REGULAR_FILE_EXTENSION = ".uvf";
+ private static final String FILENAME_DIRECTORYID = "dir";
+ private static final String DIRECTORY_METADATA_FILENAME = String.format("%s%s", FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+ private static final String BACKUP_FILENAME_DIRECTORYID = "dirid";
+ private static final String BACKUP_DIRECTORY_METADATA_FILENAME = String.format("%s%s", BACKUP_FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+
+ private static final Pattern FILENAME_PATTERN = Pattern.compile("^([A-Za-z0-9_=-]+)" + REGULAR_FILE_EXTENSION);
+
+ /**
+ * Root of vault directory
+ */
+ private final Path home;
+
+ private RevolvingMasterkey masterKey;
+
+ private Cryptor cryptor;
+ private CryptoFilename filenameProvider;
+ private CryptoDirectory directoryProvider;
+
+ private int nonceSize;
+
+ public CryptoVault(final Path home) {
+ this.home = home;
+ }
+
+ @Override
+ public AbstractVault create(final Session> session, final String region, final VaultMetadataProvider metadata) throws BackgroundException {
+ final VaultMetadataUVFProvider provider = VaultMetadataUVFProvider.cast(metadata);
+
+ final Path home = this.getHome();
+ log.debug("Create vault root directory at {}", home);
+
+ // Obtain non encrypted directory writer
+ final Directory> directory = session._getFeature(Directory.class);
+ final TransferStatus status = new TransferStatus().setRegion(region);
+ final Encryption encryption = session._getFeature(Encryption.class);
+ if(encryption != null) {
+ status.setEncryption(encryption.getDefault(home));
+ }
+ final Path vault = directory.mkdir(session._getFeature(Write.class), home, status);
+
+ final Path dataDir = new Path(vault, "d", EnumSet.of(Path.Type.directory));
+ final Path firstLevel = new Path(dataDir, provider.getDirPath().substring(0, 2), EnumSet.of(Path.Type.directory));
+ final Path secondLevel = new Path(firstLevel, provider.getDirPath().substring(2), EnumSet.of(Path.Type.directory));
+
+ directory.mkdir(session._getFeature(Write.class), dataDir, status);
+ directory.mkdir(session._getFeature(Write.class), firstLevel, status);
+ directory.mkdir(session._getFeature(Write.class), secondLevel, status);
+
+ // vault.uvf
+ new ContentWriter(session).write(new Path(home, PreferencesFactory.get().getProperty("cryptomator.vault.config.filename"),
+ EnumSet.of(Path.Type.file, Path.Type.vault)), provider.getMetadata());
+ // dir.uvf
+ new ContentWriter(session).write(new Path(secondLevel, "dir.uvf", EnumSet.of(Path.Type.file)),
+ provider.getRootDirectoryMetadata());
+
+ return this;
+ }
+
+ // load -> unlock -> open
+ @Override
+ public CryptoVault load(final Session> session, final PasswordCallback callback, final VaultMetadataProvider metadata) throws BackgroundException {
+ final JWKCallback jwk = JWKCallback.cast(callback);
+ final VaultMetadataUVFProvider metadataProvider = VaultMetadataUVFProvider.cast(metadata);
+ final JWEObjectJSON jweObject;
+ try {
+ jweObject = JWEObjectJSON.parse(new String(metadataProvider.getMetadata(), StandardCharsets.US_ASCII));
+ jweObject.decrypt(new MultiDecrypter(jwk.prompt(session.getHost(), StringUtils.EMPTY, StringUtils.EMPTY, new LoginOptions()).getKey()));
+ }
+ catch(ParseException | JOSEException e) {
+ throw new VaultException("Failure retrieving key material", e);
+ }
+ masterKey = UVFMasterkey.fromDecryptedPayload(jweObject.getPayload().toString());
+ final CryptorProvider provider = CryptorProvider.forScheme(CryptorProvider.Scheme.UVF_DRAFT);
+ log.debug("Initialized crypto provider {}", provider);
+ this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide());
+ this.filenameProvider = new CryptoFilenameV7Provider(Integer.MAX_VALUE);
+ this.directoryProvider = new CryptoDirectoryUVFProvider(this, filenameProvider);
+ this.nonceSize = 12;
+ return this;
+ }
+
+ @Override
+ public Path encrypt(Session> session, Path file, boolean metadata) throws BackgroundException {
+ final Path encrypted;
+ if(file.isFile() || metadata) {
+ if(file.getType().contains(Path.Type.vault)) {
+ log.warn("Skip file {} because it is marked as an internal vault path", file);
+ return file;
+ }
+ if(new SimplePathPredicate(file).test(this.getHome())) {
+ log.warn("Skip vault home {} because the root has no metadata file", file);
+ return file;
+ }
+ final Path parent;
+ final String filename;
+ if(file.getType().contains(Path.Type.encrypted)) {
+ final Path decrypted = file.attributes().getDecrypted();
+ parent = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent());
+ filename = this.getDirectoryProvider().toEncrypted(session, decrypted.getParent(), decrypted.getName(), decrypted.getType());
+ }
+ else {
+ parent = this.getDirectoryProvider().toEncrypted(session, file.getParent());
+ // / diff to AbstractVault.encrypt
+ filename = this.getDirectoryProvider().toEncrypted(session, file.getParent(), file.getName(), file.getType());
+ // \ diff to AbstractVault.decrypt
+ }
+ final PathAttributes attributes = new PathAttributes(file.attributes());
+ if(!file.isFile() && !metadata) {
+ // The directory is different from the metadata file used to resolve the actual folder
+ attributes.setVersionId(null);
+ attributes.setFileId(null);
+ }
+ // Translate file size
+ attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize()));
+ final EnumSet type = EnumSet.copyOf(file.getType());
+ type.remove(Path.Type.decrypted);
+ type.add(Path.Type.encrypted);
+ encrypted = new Path(parent, filename, type, attributes);
+ }
+ else {
+ if(file.getType().contains(Path.Type.encrypted)) {
+ log.warn("Skip file {} because it is already marked as an encrypted path", file);
+ return file;
+ }
+ if(file.getType().contains(Path.Type.vault)) {
+ return this.getDirectoryProvider().toEncrypted(session, this.getHome());
+ }
+ encrypted = this.getDirectoryProvider().toEncrypted(session, file);
+ }
+ // Add reference to decrypted file
+ if(!file.getType().contains(Path.Type.encrypted)) {
+ encrypted.attributes().setDecrypted(file);
+ }
+ // Add reference for vault
+ file.attributes().setVaultMetadata(this.getMetadata());
+ encrypted.attributes().setVaultMetadata(this.getMetadata());
+ return encrypted;
+ }
+
+ @Override
+ public synchronized void close() {
+ super.close();
+ cryptor.destroy();
+ }
+
+ @Override
+ public Path getMasterkeyPath() {
+ //TODO: implement
+ return null;
+ }
+
+ @Override
+ public RevolvingMasterkey getMasterkey() {
+ return masterKey;
+ }
+
+ @Override
+ public Path getConfig() {
+ //TODO: implement
+ return null;
+ }
+
+ @Override
+ public Path getHome() {
+ return home;
+ }
+
+ @Override
+ public FileHeaderCryptor getFileHeaderCryptor() {
+ return cryptor.fileHeaderCryptor();
+ }
+
+ @Override
+ public FileContentCryptor getFileContentCryptor() {
+ return cryptor.fileContentCryptor();
+ }
+
+ @Override
+ public CryptoFilename getFilenameProvider() {
+ return filenameProvider;
+ }
+
+ @Override
+ public CryptoDirectory getDirectoryProvider() {
+ return directoryProvider;
+ }
+
+ @Override
+ public Cryptor getCryptor() {
+ return cryptor;
+ }
+
+ @Override
+ public int getNonceSize() {
+ return nonceSize;
+ }
+
+ @Override
+ public Pattern getFilenamePattern() {
+ return FILENAME_PATTERN;
+ }
+
+ @Override
+ public String getRegularFileExtension() {
+ return REGULAR_FILE_EXTENSION;
+ }
+
+ @Override
+ public String getDirectoryMetadataFilename() {
+ return DIRECTORY_METADATA_FILENAME;
+ }
+
+ @Override
+ public String getBackupDirectoryMetadataFilename() {
+ return BACKUP_DIRECTORY_METADATA_FILENAME;
+ }
+
+ public DirectoryMetadata getRootDirId() {
+ return this.cryptor.directoryContentCryptor().rootDirectoryMetadata();
+ }
+
+ @Override
+ public VaultMetadata getMetadata() {
+ return new VaultMetadata(this.getHome(), VaultMetadata.Type.UVF);
+ }
+
+ @Override
+ public T getFeature(final Session> session, final Class type, final T delegate) throws UnsupportedException {
+ if(type == Directory.class) {
+ return (T) new CryptoDirectoryUVFFeature(session, (Directory) delegate, this);
+ }
+ return super.getFeature(session, type, delegate);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if(this == o) {
+ return true;
+ }
+ if(!(o instanceof CryptoVault)) {
+ return false;
+ }
+ final CryptoVault that = (CryptoVault) o;
+ return new SimplePathPredicate(home).test(that.home);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(new SimplePathPredicate(home));
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("UVFVault{");
+ sb.append("home=").append(home);
+ sb.append(", cryptor=").append(cryptor);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/DefaultVaultMetadataUVFProvider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/DefaultVaultMetadataUVFProvider.java
new file mode 100644
index 00000000000..d4f2b904594
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/DefaultVaultMetadataUVFProvider.java
@@ -0,0 +1,34 @@
+package ch.cyberduck.core.cryptomator.impl.uvf;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+public class DefaultVaultMetadataUVFProvider implements VaultMetadataUVFProvider {
+
+ @Override
+ public byte[] getMetadata() {
+ return new byte[0];
+ }
+
+ @Override
+ public byte[] getRootDirectoryMetadata() {
+ return new byte[0];
+ }
+
+ @Override
+ public String getDirPath() {
+ return "";
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/VaultMetadataUVFProvider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/VaultMetadataUVFProvider.java
new file mode 100644
index 00000000000..69e8fec931e
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/uvf/VaultMetadataUVFProvider.java
@@ -0,0 +1,36 @@
+package ch.cyberduck.core.cryptomator.impl.uvf;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.vault.VaultMetadataProvider;
+
+public interface VaultMetadataUVFProvider extends VaultMetadataProvider {
+
+ byte[] getMetadata();
+
+ byte[] getRootDirectoryMetadata();
+
+ String getDirPath();
+
+ static VaultMetadataUVFProvider cast(VaultMetadataProvider provider) {
+ if(provider instanceof VaultMetadataUVFProvider) {
+ return (VaultMetadataUVFProvider) provider;
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported metadata type " + provider.getClass());
+ }
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVault.java
new file mode 100644
index 00000000000..1d6520dae30
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVault.java
@@ -0,0 +1,521 @@
+package ch.cyberduck.core.cryptomator.impl.v8;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.Credentials;
+import ch.cyberduck.core.DescriptiveUrl;
+import ch.cyberduck.core.Host;
+import ch.cyberduck.core.LocaleFactory;
+import ch.cyberduck.core.LoginOptions;
+import ch.cyberduck.core.PasswordCallback;
+import ch.cyberduck.core.PasswordStore;
+import ch.cyberduck.core.PasswordStoreFactory;
+import ch.cyberduck.core.Path;
+import ch.cyberduck.core.Session;
+import ch.cyberduck.core.SimplePathPredicate;
+import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.AbstractVault;
+import ch.cyberduck.core.cryptomator.ContentReader;
+import ch.cyberduck.core.cryptomator.ContentWriter;
+import ch.cyberduck.core.cryptomator.CryptoAuthenticationException;
+import ch.cyberduck.core.cryptomator.CryptoDirectory;
+import ch.cyberduck.core.cryptomator.CryptoFilename;
+import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV8Provider;
+import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
+import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
+import ch.cyberduck.core.exception.BackgroundException;
+import ch.cyberduck.core.exception.LocalAccessDeniedException;
+import ch.cyberduck.core.exception.LoginCanceledException;
+import ch.cyberduck.core.exception.NotfoundException;
+import ch.cyberduck.core.features.Directory;
+import ch.cyberduck.core.features.Encryption;
+import ch.cyberduck.core.features.Vault;
+import ch.cyberduck.core.features.Write;
+import ch.cyberduck.core.preferences.Preferences;
+import ch.cyberduck.core.preferences.PreferencesFactory;
+import ch.cyberduck.core.shared.DefaultUrlProvider;
+import ch.cyberduck.core.transfer.TransferStatus;
+import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultException;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultMetadataProvider;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.cryptomator.cryptolib.api.Cryptor;
+import org.cryptomator.cryptolib.api.CryptorProvider;
+import org.cryptomator.cryptolib.api.DirectoryMetadata;
+import org.cryptomator.cryptolib.api.FileContentCryptor;
+import org.cryptomator.cryptolib.api.FileHeaderCryptor;
+import org.cryptomator.cryptolib.api.InvalidPassphraseException;
+import org.cryptomator.cryptolib.api.Masterkey;
+import org.cryptomator.cryptolib.api.PerpetualMasterkey;
+import org.cryptomator.cryptolib.common.MasterkeyFile;
+import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.InvalidClaimException;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.exceptions.SignatureVerificationException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.google.auto.service.AutoService;
+import com.google.gson.JsonParseException;
+
+@AutoService(Vault.class)
+public class CryptoVault extends AbstractVault {
+
+ private static final Logger log = LogManager.getLogger(CryptoVault.class);
+
+
+ public static final String REGULAR_FILE_EXTENSION = ".c9r";
+ public static final int VAULT_VERSION = 8;
+
+ private static final String FILENAME_DIRECTORYID = "dir";
+ private static final String DIRECTORY_METADATA_FILENAME = String.format("%s%s", FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+ private static final String BACKUP_FILENAME_DIRECTORYID = "dirid";
+ private static final String BACKUP_DIRECTORY_METADATA_FILENAME = String.format("%s%s", BACKUP_FILENAME_DIRECTORYID, REGULAR_FILE_EXTENSION);
+ private static final Pattern FILENAME_PATTERN = Pattern.compile("^([A-Za-z0-9_=-]+)" + REGULAR_FILE_EXTENSION);
+
+ private static final List SUPPORTED_VERSIONS = Arrays.asList(7, 8);
+
+ private static final String JSON_KEY_VAULTVERSION = "format";
+ private static final String JSON_KEY_CIPHERCONFIG = "cipherCombo";
+ private static final String JSON_KEY_SHORTENING_THRESHOLD = "shorteningThreshold";
+
+ private final Path home;
+ private Masterkey masterkey;
+ private final Path masterkeyPath;
+ private final byte[] pepper;
+
+ private final PasswordStore keychain = PasswordStoreFactory.get();
+ private final Preferences preferences = PreferencesFactory.get();
+
+ private final Path config;
+
+ private int nonceSize;
+ private Cryptor cryptor;
+
+ private CryptoFilename filenameProvider;
+ private CryptoDirectory directoryProvider;
+
+ public CryptoVault(final Path home) {
+ this.home = home;
+ this.masterkeyPath = new Path(home, preferences.getProperty("cryptomator.vault.masterkey.filename"), EnumSet.of(Path.Type.file, Path.Type.vault));
+ this.config = new Path(home, preferences.getProperty("cryptomator.vault.config.filename"), EnumSet.of(Path.Type.file, Path.Type.vault));
+ this.pepper = preferences.getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8);
+
+ //TODO nötig?
+ // New vault home with vault flag set for internal use
+ final EnumSet type = EnumSet.copyOf(home.getType());
+ type.add(Path.Type.vault);
+ }
+
+ public Path getHome() {
+ return home;
+ }
+
+ @Override
+ public Path getMasterkeyPath() {
+ return masterkeyPath;
+ }
+
+ public Masterkey getMasterkey() {
+ return masterkey;
+ }
+
+ @Override
+ public Path getConfig() {
+ return config;
+ }
+
+ @Override
+ public FileHeaderCryptor getFileHeaderCryptor() {
+ return cryptor.fileHeaderCryptor();
+ }
+
+ @Override
+ public FileContentCryptor getFileContentCryptor() {
+ return cryptor.fileContentCryptor();
+ }
+
+ @Override
+ public CryptoFilename getFilenameProvider() {
+ return filenameProvider;
+ }
+
+ @Override
+ public CryptoDirectory getDirectoryProvider() {
+ return directoryProvider;
+ }
+
+ @Override
+ public Cryptor getCryptor() {
+ return cryptor;
+ }
+
+ @Override
+ public int getNonceSize() {
+ return nonceSize;
+ }
+
+ @Override
+ public String getRegularFileExtension() {
+ return REGULAR_FILE_EXTENSION;
+ }
+
+ @Override
+ public String getDirectoryMetadataFilename() {
+ return DIRECTORY_METADATA_FILENAME;
+ }
+
+ @Override
+ public String getBackupDirectoryMetadataFilename() {
+ return BACKUP_DIRECTORY_METADATA_FILENAME;
+ }
+
+ @Override
+ public DirectoryMetadata getRootDirId() {
+ return this.cryptor.directoryContentCryptor().rootDirectoryMetadata();
+ }
+
+ @Override
+ public Pattern getFilenamePattern() {
+ return FILENAME_PATTERN;
+ }
+
+ public AbstractVault create(final Session> session, final String region, final VaultCredentials credentials) throws BackgroundException {
+ return this.create(session, region, new DefaultVaultMetadataV8Provider(credentials));
+ }
+
+ @Override
+ public AbstractVault create(final Session> session, final String region, final VaultMetadataProvider metadata) throws BackgroundException {
+ final VaultMetadataV8Provider provider = VaultMetadataV8Provider.cast(metadata);
+ final VaultCredentials credentials = provider.getCredentials();
+ final Host bookmark = session.getHost();
+ if(credentials.isSaved()) {
+ try {
+ keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
+ new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
+ }
+ catch(LocalAccessDeniedException e) {
+ log.error("Failure {} saving credentials for {} in password store", e, bookmark);
+ }
+ }
+ final String passphrase = credentials.getPassword();
+ final ByteArrayOutputStream mkArray = new ByteArrayOutputStream();
+ final PerpetualMasterkey mk = Masterkey.generate(FastSecureRandomProvider.get().provide());
+ final MasterkeyFileAccess access = new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide());
+ final MasterkeyFile masterkeyFile;
+ try {
+ access.persist(mk, mkArray, passphrase, VAULT_VERSION);
+ masterkeyFile = MasterkeyFile.read(new StringReader(new String(mkArray.toByteArray(), StandardCharsets.UTF_8)));
+ }
+ catch(IOException e) {
+ throw new VaultException("Failure creating master key", e);
+ }
+ log.debug("Write master key to {}", masterkeyPath);
+ // Obtain non encrypted directory writer
+ final Directory> directory = session._getFeature(Directory.class);
+ final TransferStatus status = new TransferStatus().setRegion(region);
+ final Encryption encryption = session._getFeature(Encryption.class);
+ if(encryption != null) {
+ status.setEncryption(encryption.getDefault(home));
+ }
+ final Path vault = directory.mkdir(session._getFeature(Write.class), home, status);
+ new ContentWriter(session).write(masterkeyPath, mkArray.toByteArray());
+ // Create vaultconfig.cryptomator
+ final Algorithm algorithm = Algorithm.HMAC256(mk.getEncoded());
+ final String conf = JWT.create()
+ .withJWTId(new UUIDRandomStringService().random())
+ .withKeyId(String.format("masterkeyfile:%s", masterkeyPath.getName()))
+ .withClaim(JSON_KEY_VAULTVERSION, VAULT_VERSION)
+ .withClaim(JSON_KEY_CIPHERCONFIG, CryptorProvider.Scheme.SIV_GCM.toString())
+ .withClaim(JSON_KEY_SHORTENING_THRESHOLD, CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD)
+ .sign(algorithm);
+ new ContentWriter(session).write(config, conf.getBytes(StandardCharsets.US_ASCII));
+ this.open(parseVaultConfigFromJWT(conf).withMasterkeyFile(masterkeyFile), passphrase);
+ final Path secondLevel = directoryProvider.toEncrypted(session, home);
+ final Path firstLevel = secondLevel.getParent();
+ final Path dataDir = firstLevel.getParent();
+ log.debug("Create vault root directory at {}", secondLevel);
+ directory.mkdir(session._getFeature(Write.class), dataDir, status);
+ directory.mkdir(session._getFeature(Write.class), firstLevel, status);
+ directory.mkdir(session._getFeature(Write.class), secondLevel, status);
+ return this;
+ }
+
+ private static VaultConfig parseVaultConfigFromMasterKey(final MasterkeyFile masterkeyFile) {
+ return new VaultConfig(masterkeyFile.version, CryptoFilenameV7Provider.DEFAULT_NAME_SHORTENING_THRESHOLD,
+ CryptorProvider.Scheme.SIV_CTRMAC, null, null);
+ }
+
+ @Override
+ public Vault load(final Session> session, final PasswordCallback prompt, final VaultMetadataProvider provider) throws BackgroundException {
+ final Host bookmark = session.getHost();
+ String passphrase = keychain.getPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
+ new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
+ if(null == passphrase) {
+ // Legacy
+ passphrase = keychain.getPassword(String.format("Cryptomator Passphrase %s", bookmark.getHostname()),
+ new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl());
+ }
+ return this.unlock(session, prompt, bookmark, passphrase);
+ }
+
+ public Vault unlock(final Session> session, final PasswordCallback prompt, final Host bookmark, final String passphrase) throws BackgroundException {
+ final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig = this.readVaultConfig(session);
+ this.unlock(vaultConfig, passphrase, bookmark, prompt,
+ MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())
+ );
+ return this;
+ }
+
+ public void unlock(final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig, final String passphrase, final Host bookmark, final PasswordCallback prompt,
+ final String message) throws BackgroundException {
+ final Credentials credentials;
+ if(null == passphrase) {
+ credentials = prompt.prompt(
+ bookmark, LocaleFactory.localizedString("Unlock Vault", "Cryptomator"),
+ message,
+ new LoginOptions()
+ .save(preferences.getBoolean("cryptomator.vault.keychain"))
+ .user(false)
+ .anonymous(false)
+ .icon("cryptomator.tiff")
+ .passwordPlaceholder(LocaleFactory.localizedString("Passphrase", "Cryptomator")));
+ if(null == credentials.getPassword()) {
+ throw new LoginCanceledException();
+ }
+ }
+ else {
+ credentials = new VaultCredentials(passphrase).setSaved(false);
+ }
+ try {
+ this.open(vaultConfig, credentials.getPassword());
+ if(credentials.isSaved()) {
+ log.info("Save passphrase for {}", masterkeyPath);
+ // Save password with hostname and path to masterkey.cryptomator in keychain
+ keychain.addPassword(String.format("Cryptomator Passphrase (%s)", bookmark.getCredentials().getUsername()),
+ new DefaultUrlProvider(bookmark).toUrl(masterkeyPath, EnumSet.of(DescriptiveUrl.Type.provider)).find(DescriptiveUrl.Type.provider).getUrl(), credentials.getPassword());
+ }
+ }
+ catch(CryptoAuthenticationException e) {
+ this.unlock(vaultConfig, null, bookmark, prompt, String.format("%s %s.", e.getDetail(),
+ MessageFormat.format(LocaleFactory.localizedString("Provide your passphrase to unlock the Cryptomator Vault {0}", "Cryptomator"), home.getName())));
+ }
+ }
+
+ private ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig readVaultConfig(final Session> session) throws BackgroundException {
+ final MasterkeyFile masterkeyFile = this.readMasterkeyFile(session, masterkeyPath);
+ try {
+ return parseVaultConfigFromJWT(new ContentReader(session).read(config)).withMasterkeyFile(masterkeyFile);
+ }
+ catch(NotfoundException e) {
+ log.debug("Ignore failure reading vault configuration {}", config);
+ return parseVaultConfigFromMasterKey(masterkeyFile).withMasterkeyFile(masterkeyFile);
+ }
+ }
+
+ public static ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig parseVaultConfigFromJWT(final String token) {
+ final DecodedJWT decoded = JWT.decode(token);
+ return new ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig(
+ decoded.getClaim(JSON_KEY_VAULTVERSION).asInt(),
+ decoded.getClaim(JSON_KEY_SHORTENING_THRESHOLD).asInt(),
+ CryptorProvider.Scheme.valueOf(decoded.getClaim(JSON_KEY_CIPHERCONFIG).asString()),
+ decoded.getAlgorithm(), decoded);
+ }
+
+ private MasterkeyFile readMasterkeyFile(final Session> session, final Path file) throws BackgroundException {
+ log.debug("Read master key {}", file);
+ try(Reader reader = new ContentReader(session).getReader(file)) {
+ return MasterkeyFile.read(reader);
+ }
+ catch(JsonParseException | IllegalArgumentException | IllegalStateException | IOException e) {
+ throw new VaultException(String.format("Failure reading vault master key file %s", file.getName()), e);
+ }
+ }
+
+ protected void open(final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException {
+ try {
+ final PerpetualMasterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase);
+ this.open(vaultConfig, masterKey);
+ }
+ catch(IllegalArgumentException | IOException e) {
+ throw new VaultException("Failure reading key file", e);
+ }
+ catch(InvalidPassphraseException e) {
+ throw new CryptoAuthenticationException("Failure to decrypt master key file", e);
+ }
+ }
+
+ protected void open(final ch.cyberduck.core.cryptomator.impl.v8.CryptoVault.VaultConfig vaultConfig, final PerpetualMasterkey masterKey) throws BackgroundException {
+ if(!SUPPORTED_VERSIONS.contains(vaultConfig.vaultVersion())) {
+ throw new VaultException(String.format("Unsupported vault version %d", vaultConfig.vaultVersion()));
+ }
+ final CryptorProvider provider = CryptorProvider.forScheme(vaultConfig.getCipherCombo());
+ log.debug("Initialized crypto provider {}", provider);
+ vaultConfig.verify(masterKey.getEncoded(), VAULT_VERSION);
+ this.masterkey = masterKey;
+ this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide());
+ this.filenameProvider = new CryptoFilenameV7Provider(vaultConfig.getShorteningThreshold());
+ this.directoryProvider = new CryptoDirectoryV8Provider(this, this.filenameProvider);
+ this.nonceSize = vaultConfig.getNonceSize();
+ }
+
+ private PerpetualMasterkey getMasterKey(final MasterkeyFile mkFile, final CharSequence passphrase) throws IOException {
+ final StringWriter writer = new StringWriter();
+ mkFile.write(writer);
+ return new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide()).load(
+ new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase);
+ }
+
+ @Override
+ public VaultMetadata getMetadata() {
+ return new VaultMetadata(this.getHome(), VaultMetadata.Type.V8);
+ }
+
+ @Override
+ public synchronized void close() {
+ super.close();
+ cryptor = null;
+ }
+
+ public static class VaultConfig {
+
+ private final int version;
+ private final int shorteningThreshold;
+ private final CryptorProvider.Scheme cipherCombo;
+ private final String algorithm;
+ private final DecodedJWT token;
+ private MasterkeyFile mkfile;
+
+ public VaultConfig(int version, int shorteningThreshold, CryptorProvider.Scheme cipherCombo, String algorithm, DecodedJWT token) {
+ this.version = version;
+ this.shorteningThreshold = shorteningThreshold;
+ this.cipherCombo = cipherCombo;
+ this.algorithm = algorithm;
+ this.token = token;
+ }
+
+ public int vaultVersion() {
+ return version;
+ }
+
+ public VaultConfig withMasterkeyFile(final MasterkeyFile mkfile) {
+ this.mkfile = mkfile;
+ return this;
+ }
+
+ public MasterkeyFile getMkfile() {
+ return mkfile;
+ }
+
+ public int getShorteningThreshold() {
+ return shorteningThreshold;
+ }
+
+ public CryptorProvider.Scheme getCipherCombo() {
+ return cipherCombo;
+ }
+
+ public int getNonceSize() throws VaultException {
+ switch(cipherCombo) {
+ case SIV_CTRMAC:
+ return 16;
+ case SIV_GCM:
+ return 12;
+ default:
+ throw new VaultException(String.format("Unsupported cipher scheme %s", cipherCombo));
+ }
+ }
+
+ private Algorithm initAlgorithm(byte[] rawKey) throws VaultException {
+ switch(algorithm) {
+ case "HS256":
+ return Algorithm.HMAC256(rawKey);
+ case "HS384":
+ return Algorithm.HMAC384(rawKey);
+ case "HS512":
+ return Algorithm.HMAC512(rawKey);
+ default:
+ throw new VaultException(String.format("Unsupported signature algorithm %s", algorithm));
+ }
+ }
+
+ public void verify(byte[] rawKey, int expectedVaultVersion) throws VaultException {
+ try {
+ if(token == null) {
+ return;
+ }
+ JWTVerifier verifier = JWT.require(initAlgorithm(rawKey))
+ .withClaim(JSON_KEY_VAULTVERSION, expectedVaultVersion)
+ .build();
+ verifier.verify(token);
+ }
+ catch(SignatureVerificationException e) {
+ throw new VaultException("Invalid JWT signature", e);
+ }
+ catch(InvalidClaimException e) {
+ throw new VaultException(String.format("Expected vault config for version %d", expectedVaultVersion), e);
+ }
+ catch(JWTVerificationException e) {
+ throw new VaultException(String.format("Failed to verify vault config %s", token), e);
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if(this == o) {
+ return true;
+ }
+ if(!(o instanceof CryptoVault)) {
+ return false;
+ }
+ final CryptoVault that = (CryptoVault) o;
+ return new SimplePathPredicate(home).test(that.home);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(new SimplePathPredicate(home));
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("CryptoVault{");
+ sb.append("home=").append(home);
+ sb.append(", cryptor=").append(cryptor);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/DefaultVaultMetadataV8Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/DefaultVaultMetadataV8Provider.java
new file mode 100644
index 00000000000..bf338ddc976
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/DefaultVaultMetadataV8Provider.java
@@ -0,0 +1,32 @@
+package ch.cyberduck.core.cryptomator.impl.v8;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.vault.VaultCredentials;
+
+public class DefaultVaultMetadataV8Provider implements VaultMetadataV8Provider {
+
+ private VaultCredentials credentials;
+
+ public DefaultVaultMetadataV8Provider(final VaultCredentials credentials) {
+ this.credentials = credentials;
+ }
+
+ @Override
+ public VaultCredentials getCredentials() {
+ return credentials;
+ }
+}
diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/VaultMetadataV8Provider.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/VaultMetadataV8Provider.java
new file mode 100644
index 00000000000..cd6de72f367
--- /dev/null
+++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/v8/VaultMetadataV8Provider.java
@@ -0,0 +1,33 @@
+package ch.cyberduck.core.cryptomator.impl.v8;
+
+/*
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
+ * https://cyberduck.io/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadataProvider;
+
+public interface VaultMetadataV8Provider extends VaultMetadataProvider {
+
+ VaultCredentials getCredentials();
+
+ static VaultMetadataV8Provider cast(VaultMetadataProvider provider) {
+ if(provider instanceof VaultMetadataV8Provider) {
+ return (VaultMetadataV8Provider) provider;
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported metadata type " + provider.getClass());
+ }
+ }
+}
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java
index a539352f90f..4935d094072 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoChecksumComputeTest.java
@@ -20,6 +20,7 @@
import ch.cyberduck.core.Path;
import ch.cyberduck.core.TestProtocol;
import ch.cyberduck.core.cryptomator.features.CryptoChecksumCompute;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Write;
@@ -61,7 +62,6 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
cryptomator.create(session, null, new VaultCredentials("test"));
final ByteBuffer header = cryptomator.getFileHeaderCryptor().encryptHeader(cryptomator.getFileHeaderCryptor().create());
// DEFAULT_PIPE_SIZE=1024
- final Path file = new Path(vault, "f", EnumSet.of(Path.Type.file));
final SHA256ChecksumCompute sha = new SHA256ChecksumCompute();
final CryptoChecksumCompute compute = new CryptoChecksumCompute(sha, cryptomator);
final RandomNonceGenerator nonces = new RandomNonceGenerator(cryptomator.getNonceSize());
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java
index eff5c2e279a..a5f848ade45 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoOutputStreamTest.java
@@ -19,6 +19,7 @@
import ch.cyberduck.core.NullSession;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.TestProtocol;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Write;
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java
index ecacf7650c8..3c2d81679a3 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoWriteFeatureTest.java
@@ -19,6 +19,7 @@
import ch.cyberduck.core.NullSession;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.TestProtocol;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Write;
import ch.cyberduck.core.transfer.TransferStatus;
@@ -33,43 +34,6 @@
public class CryptoWriteFeatureTest {
- @Test
- public void testCiphertextSize_CTR() throws Exception {
- final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
- final NullSession session = new NullSession(new Host(new TestProtocol())) {
- @Override
- @SuppressWarnings("unchecked")
- public T _getFeature(final Class type) {
- if(type == Directory.class) {
- return (T) new Directory() {
-
- @Override
- public Path mkdir(final Write writer, final Path folder, final TransferStatus status) {
- assertTrue(folder.equals(home) || folder.isChild(home));
- return folder;
- }
- };
- }
- return super._getFeature(type);
- }
- };
- final CryptoVault vault = new CryptoVault(home);
- vault.create(session, null, new VaultCredentials("test"), CryptoVault.VAULT_VERSION_DEPRECATED);
- int headerSize = vault.getFileHeaderCryptor().headerSize();
- // zero file size
- assertEquals(headerSize, vault.toCiphertextSize(0L, 0));
- // one-byte file
- assertEquals(headerSize + 48 + 1, vault.toCiphertextSize(0L, 1));
- // file with chunk size length
- assertEquals(headerSize + vault.getFileContentCryptor().ciphertextChunkSize(), vault.toCiphertextSize(0L, vault.getFileContentCryptor().cleartextChunkSize()));
- // file with chunk size length + 1
- assertEquals(headerSize + vault.getFileContentCryptor().ciphertextChunkSize() + 48 + 1, vault.toCiphertextSize(0L, vault.getFileContentCryptor().cleartextChunkSize() + 1));
- // file with 2 * chunk size length
- assertEquals(headerSize + 2 * vault.getFileContentCryptor().ciphertextChunkSize(), vault.toCiphertextSize(0L, 2 * vault.getFileContentCryptor().cleartextChunkSize()));
- // file with 2 * chunk size length + 100
- assertEquals(headerSize + 2 * vault.getFileContentCryptor().ciphertextChunkSize() + 48 + 100, vault.toCiphertextSize(0L, 2 * vault.getFileContentCryptor().cleartextChunkSize() + 100));
- }
-
@Test
public void testCiphertextSize_GCM() throws Exception {
final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
@@ -91,7 +55,7 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
}
};
final CryptoVault vault = new CryptoVault(home);
- vault.create(session, null, new VaultCredentials("test"), CryptoVault.VAULT_VERSION);
+ vault.create(session, null, new VaultCredentials("test"));
int headerSize = vault.getFileHeaderCryptor().headerSize();
// zero file size
assertEquals(headerSize, vault.toCiphertextSize(0L, 0));
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java
index f88038fc29e..94deb11d3d5 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptorCacheTest.java
@@ -19,10 +19,11 @@
import org.cryptomator.cryptolib.api.FileNameCryptor;
import org.junit.Test;
+import java.nio.charset.StandardCharsets;
+
import com.google.common.io.BaseEncoding;
import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
public class CryptorCacheTest {
@@ -31,10 +32,10 @@ public class CryptorCacheTest {
public void TestHashDirectoryId() {
final FileNameCryptor mock = mock(FileNameCryptor.class);
final CryptorCache cryptor = new CryptorCache(mock);
- when(mock.hashDirectoryId(anyString())).thenReturn("hashed");
- assertEquals("hashed", cryptor.hashDirectoryId("id"));
- assertEquals("hashed", cryptor.hashDirectoryId("id"));
- verify(mock, times(1)).hashDirectoryId(anyString());
+ when(mock.hashDirectoryId(any(byte[].class))).thenReturn("hashed");
+ assertEquals("hashed", cryptor.hashDirectoryId("id".getBytes(StandardCharsets.US_ASCII)));
+ assertEquals("hashed", cryptor.hashDirectoryId("id".getBytes(StandardCharsets.US_ASCII)));
+ verify(mock, times(1)).hashDirectoryId(any(byte[].class));
verifyNoMoreInteractions(mock);
}
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java
index 2bde499b600..bcc398e2bd5 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeatureTest.java
@@ -22,7 +22,7 @@
import ch.cyberduck.core.NullSession;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.TestProtocol;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
import ch.cyberduck.core.features.Bulk;
import ch.cyberduck.core.features.Directory;
import ch.cyberduck.core.features.Write;
@@ -88,7 +88,7 @@ public boolean test(final TransferItem item) {
return item.remote.isDirectory();
}
}).findFirst().get().remote;
- final String directoryId = encryptedDirectory.attributes().getDirectoryId();
+ final byte[] directoryId = encryptedDirectory.attributes().getDirectoryId();
assertNotNull(directoryId);
for(TransferItem file : pre.keySet().stream().filter(new Predicate() {
@Override
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java
index b0f53977c41..fd719bfdad9 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeatureTest.java
@@ -23,12 +23,13 @@
import ch.cyberduck.core.NullSession;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.TestProtocol;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Read;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadataProvider;
import org.apache.commons.io.IOUtils;
import org.cryptomator.cryptolib.api.CryptorProvider;
@@ -38,12 +39,12 @@
import java.nio.charset.Charset;
import java.util.EnumSet;
-import static ch.cyberduck.core.cryptomator.CryptoVault.VAULT_VERSION;
-import static ch.cyberduck.core.cryptomator.CryptoVaultTest.createJWT;
+import static ch.cyberduck.core.cryptomator.impl.v8.CryptoVaultTest.createJWT;
import static org.junit.Assert.assertEquals;
public class CryptoReadFeatureTest {
+ //TODO prüfen, ob CTR in 7 vorkommen kann. oder nur GCM
@Test
public void testCalculations_CTR() throws Exception {
final NullSession session = new NullSession(new Host(new TestProtocol())) {
@@ -61,7 +62,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
" \"primaryMasterKey\": \"Q7pGo1l0jmZssoQh9rXFPKJE9NIXvPbL+HcnVSR9CHdkeR8AwgFtcw==\",\n" +
" \"hmacMasterKey\": \"xzBqT4/7uEcQbhHFLC0YmMy4ykVKbuvJEA46p1Xm25mJNuTc20nCbw==\",\n" +
" \"versionMac\": \"hlNr3dz/CmuVajhaiGyCem9lcVIUjDfSMLhjppcXOrM=\",\n" +
- " \"version\": 6\n" +
+ " \"version\": 7\n" +
"}";
if("masterkey.cryptomator".equals(file.getName())) {
return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
@@ -87,10 +88,11 @@ public Credentials prompt(final Host bookmark, final String title, final String
final LoginOptions options) {
return new VaultCredentials("vault");
}
+ }, new VaultMetadataProvider() {
}).
getHome());
- CryptoReadFeature read = new CryptoReadFeature(null, null, vault);
+ CryptoReadFeature read = new CryptoReadFeature(session, null, vault);
{
assertEquals(0, read.chunk(0));
@@ -141,7 +143,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
}
if("vault.cryptomator".equals(file.getName())) {
- return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault"), Charset.defaultCharset());
+ return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault"), Charset.defaultCharset());
}
throw new NotfoundException(String.format("%s not found", file.getName()));
}
@@ -162,6 +164,7 @@ public boolean offset(final Path file) {
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
return new VaultCredentials("vault");
}
+ }, new VaultMetadataProvider() {
}).getHome());
CryptoReadFeature read = new CryptoReadFeature(null, null, vault);
{
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java
deleted file mode 100644
index a2f78fa324e..00000000000
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package ch.cyberduck.core.cryptomator.impl;
-
-/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.ConnectionCallback;
-import ch.cyberduck.core.Credentials;
-import ch.cyberduck.core.DisabledPasswordCallback;
-import ch.cyberduck.core.Host;
-import ch.cyberduck.core.LoginOptions;
-import ch.cyberduck.core.NullSession;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.TestProtocol;
-import ch.cyberduck.core.cryptomator.CryptoDirectory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
-import ch.cyberduck.core.exception.BackgroundException;
-import ch.cyberduck.core.exception.NotfoundException;
-import ch.cyberduck.core.features.Read;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.VaultCredentials;
-
-import org.apache.commons.io.IOUtils;
-import org.junit.Test;
-
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.EnumSet;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-public class CryptoDirectoryV6ProviderTest {
-
- @Test(expected = NotfoundException.class)
- public void testToEncryptedInvalidArgument() throws Exception {
- final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
- final CryptoVault vault = new CryptoVault(home);
- final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, vault);
- provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/vault/f", EnumSet.of(Path.Type.file)));
- }
-
- @Test(expected = NotfoundException.class)
- public void testToEncryptedInvalidPath() throws Exception {
- final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
- final CryptoVault vault = new CryptoVault(home);
- final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, vault);
- provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/", EnumSet.of(Path.Type.directory)));
- }
-
- @Test
- public void testToEncryptedDirectory() throws Exception {
- final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
- final NullSession session = new NullSession(new Host(new TestProtocol())) {
- @Override
- @SuppressWarnings("unchecked")
- public T _getFeature(final Class type) {
- if(type == Read.class) {
- return (T) new Read() {
- @Override
- public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
- final String masterKey = "{\n" +
- " \"scryptSalt\": \"NrC7QGG/ouc=\",\n" +
- " \"scryptCostParam\": 16384,\n" +
- " \"scryptBlockSize\": 8,\n" +
- " \"primaryMasterKey\": \"Q7pGo1l0jmZssoQh9rXFPKJE9NIXvPbL+HcnVSR9CHdkeR8AwgFtcw==\",\n" +
- " \"hmacMasterKey\": \"xzBqT4/7uEcQbhHFLC0YmMy4ykVKbuvJEA46p1Xm25mJNuTc20nCbw==\",\n" +
- " \"versionMac\": \"hlNr3dz/CmuVajhaiGyCem9lcVIUjDfSMLhjppcXOrM=\",\n" +
- " \"version\": 5\n" +
- "}";
- if("masterkey.cryptomator".equals(file.getName())) {
- return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
- }
- throw new NotfoundException(String.format("%s not found", file.getName()));
- }
-
- @Override
- public boolean offset(final Path file) {
- return false;
- }
- };
- }
- return super._getFeature(type);
- }
- };
- final CryptoVault vault = new CryptoVault(home);
- vault.load(session, new DisabledPasswordCallback() {
- @Override
- public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
- return new VaultCredentials("vault");
- }
- });
- final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, vault);
- assertNotNull(provider.toEncrypted(session, null, home));
- final Path f = new Path("/vault/f", EnumSet.of(Path.Type.directory));
- assertNotNull(provider.toEncrypted(session, null, f));
- assertEquals(provider.toEncrypted(session, null, f), provider.toEncrypted(session, null, f));
- }
-}
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8ProviderTest.java
similarity index 60%
rename from cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java
rename to cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8ProviderTest.java
index ee372bed4d0..9cf9fff1555 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV8ProviderTest.java
@@ -24,12 +24,14 @@
import ch.cyberduck.core.Path;
import ch.cyberduck.core.TestProtocol;
import ch.cyberduck.core.cryptomator.CryptoDirectory;
-import ch.cyberduck.core.cryptomator.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVault;
+import ch.cyberduck.core.cryptomator.impl.v8.CryptoVaultTest;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.NotfoundException;
import ch.cyberduck.core.features.Read;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadataProvider;
import org.apache.commons.io.IOUtils;
import org.cryptomator.cryptolib.api.CryptorProvider;
@@ -37,34 +39,36 @@
import java.io.InputStream;
import java.nio.charset.Charset;
+import java.security.SecureRandom;
import java.util.EnumSet;
-import static ch.cyberduck.core.cryptomator.CryptoVault.VAULT_VERSION;
-import static ch.cyberduck.core.cryptomator.CryptoVaultTest.createJWT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-public class CryptoDirectoryV7ProviderTest {
+public class CryptoDirectoryV8ProviderTest {
@Test(expected = NotfoundException.class)
public void testToEncryptedInvalidArgument() throws Exception {
final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
- final CryptoVault vault = new CryptoVault(home);
- final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, vault);
- provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/vault/f", EnumSet.of(Path.Type.file)));
+ final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_GCM);
+ final SecureRandom random = new SecureRandom();
+ final NullSession session = new NullSession(new Host(new TestProtocol()));
+ final CryptoDirectory provider = new CryptoDirectoryV8Provider(new CryptoVault(home), new CryptoFilenameV7Provider());
+ provider.toEncrypted(session, new Path("/vault/f", EnumSet.of(Path.Type.file)));
}
@Test(expected = NotfoundException.class)
public void testToEncryptedInvalidPath() throws Exception {
final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
- final CryptoVault vault = new CryptoVault(home);
- final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, vault);
- provider.toEncrypted(new NullSession(new Host(new TestProtocol())), null, new Path("/", EnumSet.of(Path.Type.directory)));
+ final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_GCM);
+ final SecureRandom random = new SecureRandom();
+ final NullSession session = new NullSession(new Host(new TestProtocol()));
+ final CryptoDirectory provider = new CryptoDirectoryV8Provider(new CryptoVault(home), new CryptoFilenameV7Provider());
+ provider.toEncrypted(session, new Path("/", EnumSet.of(Path.Type.directory)));
}
@Test
public void testToEncryptedDirectory() throws Exception {
- final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
final NullSession session = new NullSession(new Host(new TestProtocol())) {
@Override
@SuppressWarnings("unchecked")
@@ -74,19 +78,20 @@ public T _getFeature(final Class type) {
@Override
public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
final String masterKey = "{\n" +
- " \"scryptSalt\": \"NrC7QGG/ouc=\",\n" +
- " \"scryptCostParam\": 16384,\n" +
+ " \"version\": 8,\n" +
+ " \"scryptSalt\": \"RVAAirkArDU=\",\n" +
+ " \"scryptCostParam\": 32768,\n" +
" \"scryptBlockSize\": 8,\n" +
- " \"primaryMasterKey\": \"Q7pGo1l0jmZssoQh9rXFPKJE9NIXvPbL+HcnVSR9CHdkeR8AwgFtcw==\",\n" +
- " \"hmacMasterKey\": \"xzBqT4/7uEcQbhHFLC0YmMy4ykVKbuvJEA46p1Xm25mJNuTc20nCbw==\",\n" +
- " \"versionMac\": \"hlNr3dz/CmuVajhaiGyCem9lcVIUjDfSMLhjppcXOrM=\",\n" +
- " \"version\": 8\n" +
+ " \"primaryMasterKey\": \"+03NkJNWVsJ9Tb1CTpKhXyfINzjDirFFI+iJLOWIOySyxB+abpx34Q==\",\n" +
+ " \"hmacMasterKey\": \"aMoDtn7Y6kIXxyHo2zl47p5jCYTlRnfx3l3AMgULmIDSYAxVAraSgg==\",\n" +
+ " \"versionMac\": \"FzirA8UhwCmS5RsC4JvxbO+ZBxaCbIkzqD2Ocagd+A8=\"\n" +
"}";
+
if("masterkey.cryptomator".equals(file.getName())) {
return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
}
if("vault.cryptomator".equals(file.getName())) {
- return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault"), Charset.defaultCharset());
+ return IOUtils.toInputStream(CryptoVaultTest.createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
}
throw new NotfoundException(String.format("%s not found", file.getName()));
}
@@ -100,17 +105,19 @@ public boolean offset(final Path file) {
return super._getFeature(type);
}
};
+ final Path home = new Path("/vault", EnumSet.of((Path.Type.directory)));
final CryptoVault vault = new CryptoVault(home);
vault.load(session, new DisabledPasswordCallback() {
@Override
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
- return new VaultCredentials("vault");
+ return new VaultCredentials("vault123");
}
+ }, new VaultMetadataProvider() {
});
- final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, vault);
- assertNotNull(provider.toEncrypted(session, null, home));
+ final CryptoDirectory provider = new CryptoDirectoryV8Provider(vault, new CryptoFilenameV7Provider());
+ assertNotNull(provider.toEncrypted(session, home));
final Path f = new Path("/vault/f", EnumSet.of(Path.Type.directory));
- assertNotNull(provider.toEncrypted(session, null, f));
- assertEquals(provider.toEncrypted(session, null, f), provider.toEncrypted(session, null, f));
+ assertNotNull(provider.toEncrypted(session, f));
+ assertEquals(provider.toEncrypted(session, f), provider.toEncrypted(session, f));
}
}
diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVaultTest.java
similarity index 89%
rename from cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java
rename to cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVaultTest.java
index 0a9361a6de7..28d68719e88 100644
--- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java
+++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/v8/CryptoVaultTest.java
@@ -1,7 +1,7 @@
-package ch.cyberduck.core.cryptomator;
+package ch.cyberduck.core.cryptomator.impl.v8;
/*
- * Copyright (c) 2002-2020 iterate GmbH. All rights reserved.
+ * Copyright (c) 2002-2025 iterate GmbH. All rights reserved.
* https://cyberduck.io/
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,7 @@
import ch.cyberduck.core.SerializerFactory;
import ch.cyberduck.core.TestProtocol;
import ch.cyberduck.core.UUIDRandomStringService;
+import ch.cyberduck.core.cryptomator.CryptoInvalidFilesizeException;
import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider;
import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider;
import ch.cyberduck.core.exception.BackgroundException;
@@ -39,10 +40,12 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
+import ch.cyberduck.core.vault.VaultMetadataProvider;
import org.apache.commons.io.IOUtils;
import org.cryptomator.cryptolib.api.CryptorProvider;
-import org.cryptomator.cryptolib.api.Masterkey;
+import org.cryptomator.cryptolib.api.PerpetualMasterkey;
import org.cryptomator.cryptolib.common.MasterkeyFile;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.junit.Test;
@@ -60,7 +63,6 @@
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
-import static ch.cyberduck.core.cryptomator.CryptoVault.VAULT_VERSION;
import static org.junit.Assert.*;
public class CryptoVaultTest {
@@ -89,7 +91,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
}
if("vault.cryptomator".equals(file.getName())) {
- return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+ return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
}
throw new NotfoundException(String.format("%s not found", file.getName()));
}
@@ -110,6 +112,7 @@ public boolean offset(final Path file) {
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
return new VaultCredentials("vault123");
}
+ }, new VaultMetadataProvider() {
});
assertTrue(vault.getFileContentCryptor().getClass().getName().contains("v2"));
assertTrue(vault.getFileHeaderCryptor().getClass().getName().contains("v2"));
@@ -118,8 +121,10 @@ public Credentials prompt(final Host bookmark, final String title, final String
assertEquals(vault.encrypt(session, home), vault.encrypt(session, home));
final Path directory = new Path(home, "dir", EnumSet.of(Path.Type.directory));
assertNull(directory.attributes().getVault());
- assertEquals(home, vault.encrypt(session, directory).attributes().getVault());
- assertEquals(home, directory.attributes().getVault());
+ assertEquals(home, vault.encrypt(session, directory).attributes().getVaultMetadata().root);
+ assertEquals(VaultMetadata.Type.V8, vault.encrypt(session, directory).attributes().getVaultMetadata().type);
+ assertEquals(home, directory.attributes().getVaultMetadata().root);
+ assertEquals(VaultMetadata.Type.V8, directory.attributes().getVaultMetadata().type);
assertEquals(vault.encrypt(session, directory), vault.encrypt(session, directory));
assertEquals(new Path(home, directory.getName(), EnumSet.of(Path.Type.directory, Path.Type.decrypted)), vault.decrypt(session, vault.encrypt(session, directory, true)));
final Path placeholder = new Path(home, "placeholder", EnumSet.of(Path.Type.directory, Path.Type.placeholder));
@@ -171,7 +176,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
}
if("vault.cryptomator".equals(file.getName())) {
- return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+ return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
}
throw new NotfoundException(String.format("%s not found", file.getName()));
}
@@ -192,6 +197,7 @@ public boolean offset(final Path file) {
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
return new VaultCredentials("vault123");
}
+ }, new VaultMetadataProvider() {
}).getHome());
assertTrue(vault.getFileContentCryptor().getClass().getName().contains("v2"));
assertTrue(vault.getFileHeaderCryptor().getClass().getName().contains("v2"));
@@ -222,7 +228,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
}
if("vault.cryptomator".equals(file.getName())) {
- return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+ return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
}
throw new NotfoundException(String.format("%s not found", file.getName()));
}
@@ -243,6 +249,7 @@ public boolean offset(final Path file) {
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
return new VaultCredentials("vault123");
}
+ }, new VaultMetadataProvider() {
}).getHome());
assertEquals(Vault.State.open, vault.getState());
assertEquals(home, new PathDictionary<>().deserialize(home.serialize(SerializerFactory.get())));
@@ -272,7 +279,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
return IOUtils.toInputStream(masterKey, Charset.defaultCharset());
}
if("vault.cryptomator".equals(file.getName())) {
- return IOUtils.toInputStream(createJWT(masterKey, VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
+ return IOUtils.toInputStream(createJWT(masterKey, CryptoVault.VAULT_VERSION, CryptorProvider.Scheme.SIV_GCM, "vault123"), Charset.defaultCharset());
}
throw new NotfoundException(String.format("%s not found", file.getName()));
}
@@ -302,6 +309,7 @@ public Credentials prompt(final Host bookmark, final String title, final String
throw new LoginCanceledException();
}
}
+ }, new VaultMetadataProvider() {
});
fail();
}
@@ -352,6 +360,7 @@ public boolean offset(final Path file) {
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException {
throw new LoginCanceledException();
}
+ }, new VaultMetadataProvider() {
});
fail();
}
@@ -401,6 +410,7 @@ public boolean offset(final Path file) {
public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
return new VaultCredentials(null);
}
+ }, new VaultMetadataProvider() {
});
fail();
}
@@ -433,51 +443,6 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
vault.create(session, null, new VaultCredentials("test"));
}
- @Test
- public void testCleartextSizeV6() throws Exception {
- final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
- final NullSession session = new NullSession(new Host(new TestProtocol())) {
- @Override
- @SuppressWarnings("unchecked")
- public T _getFeature(final Class type) {
- if(type == Directory.class) {
- return (T) new Directory() {
-
- @Override
- public Path mkdir(final Write writer, final Path folder, final TransferStatus status) {
- assertTrue(folder.equals(home) || folder.isChild(home));
- return folder;
- }
- };
- }
- return super._getFeature(type);
- }
- };
- final CryptoVault vault = new CryptoVault(
- home);
- vault.create(session, null, new VaultCredentials("test"), 6);
- // zero ciphertextFileSize
- try {
- vault.toCleartextSize(0L, 0);
- fail();
- }
- catch(CryptoInvalidFilesizeException e) {
- }
- // ciphertextFileSize == headerSize
- assertEquals(0L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize()));
- // ciphertextFileSize == headerSize + 1
- try {
- vault.toCleartextSize(0L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize()) + 1);
- fail();
- }
- catch(CryptoInvalidFilesizeException e) {
- }
- // ciphertextFileSize == headerSize + chunkHeaderSize + 1
- assertEquals(1L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize() + 48 + 1));
- // ciphertextFileSize == headerSize + (32768 + chunkHeaderSize) + (1 + chunkHeaderSize) + 1
- assertEquals(32769L, vault.toCleartextSize(0L, vault.getFileHeaderCryptor().headerSize() + (32768 + 48) + (1 + 48)));
- }
-
@Test
public void testCleartextSizeV8() throws Exception {
final Path home = new Path("/vault", EnumSet.of(Path.Type.directory));
@@ -498,8 +463,7 @@ public Path mkdir(final Write writer, final Path folder, final TransferStatus st
return super._getFeature(type);
}
};
- final CryptoVault vault = new CryptoVault(
- home);
+ final CryptoVault vault = new CryptoVault(home);
vault.create(session, null, new VaultCredentials("test"));
// zero ciphertextFileSize
try {
@@ -548,8 +512,7 @@ public boolean isSupported(final Path workdir, final String name) {
return super._getFeature(type);
}
};
- final CryptoVault vault = new CryptoVault(
- home);
+ final CryptoVault vault = new CryptoVault(home);
vault.create(session, null, new VaultCredentials("test"));
for(int i = 0; i < 26000000; i++) {
assertEquals(i, vault.toCleartextSize(0L, vault.toCiphertextSize(0L, i)));
@@ -564,7 +527,7 @@ public static String createJWT(final String masterkeyCryptomator,
final MasterkeyFile mkFile = MasterkeyFile.read(new StringReader(masterkeyCryptomator));
final StringWriter writer = new StringWriter();
mkFile.write(writer);
- final Masterkey masterkey = new MasterkeyFileAccess(PreferencesFactory.get().getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8),
+ final PerpetualMasterkey masterkey = new MasterkeyFileAccess(PreferencesFactory.get().getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8),
FastSecureRandomProvider.get().provide()).load(new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase);
final Algorithm algorithm = Algorithm.HMAC256(masterkey.getEncoded());
return JWT.create()
diff --git a/ctera/pom.xml b/ctera/pom.xml
index 249b2e6f7db..032c6818ab7 100644
--- a/ctera/pom.xml
+++ b/ctera/pom.xml
@@ -18,7 +18,7 @@
ch.cyberduck
parent
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
ctera
jar
diff --git a/deepbox/pom.xml b/deepbox/pom.xml
index 1d7dd42f32d..056fa4db5d7 100644
--- a/deepbox/pom.xml
+++ b/deepbox/pom.xml
@@ -19,7 +19,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
deepbox
diff --git a/defaults/pom.xml b/defaults/pom.xml
index c27c855ec83..288193754ca 100644
--- a/defaults/pom.xml
+++ b/defaults/pom.xml
@@ -19,7 +19,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
defaults
diff --git a/defaults/src/main/resources/default.properties b/defaults/src/main/resources/default.properties
index f1ef70f9a72..795f929637c 100644
--- a/defaults/src/main/resources/default.properties
+++ b/defaults/src/main/resources/default.properties
@@ -709,13 +709,14 @@ terminal.command.ssh=ssh -t {0} {1}@{2} -p {3} \"cd {4} && exec \\$SHELL -l\"
threading.pool.size.max=20
threading.pool.keepalive.seconds=60
cryptomator.enable=true
-cryptomator.vault.version=8
+cryptomator.vault.default=v8
cryptomator.vault.autodetect=true
cryptomator.vault.autodetect.filecount=10
# Load and add to registry when vault is referenced in file attributes
cryptomator.vault.autoload=true
cryptomator.vault.masterkey.filename=masterkey.cryptomator
cryptomator.vault.config.filename=vault.cryptomator
+cryptomator.vault.config.filename.uvf=vault.uvf
cryptomator.vault.pepper=
cryptomator.vault.skip.regex=dirid.c9r
cryptomator.cache.size=1000
diff --git a/defaults/src/main/resources/default/log4j.xml b/defaults/src/main/resources/default/log4j.xml
index a02f90d3f10..ce4787cd17e 100644
--- a/defaults/src/main/resources/default/log4j.xml
+++ b/defaults/src/main/resources/default/log4j.xml
@@ -1,11 +1,11 @@
-
+
+
+
-
-
+
+
+
diff --git a/dracoon/pom.xml b/dracoon/pom.xml
index a3e4aec8bfc..c42aabe7322 100644
--- a/dracoon/pom.xml
+++ b/dracoon/pom.xml
@@ -19,7 +19,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
dracoon
diff --git a/dropbox/pom.xml b/dropbox/pom.xml
index 40bf3f4ffbf..ac934f31f3b 100644
--- a/dropbox/pom.xml
+++ b/dropbox/pom.xml
@@ -18,7 +18,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
4.0.0
dropbox
diff --git a/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java b/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java
index 361a6586246..552af6ebe8c 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/AbstractDropboxTest.java
@@ -15,12 +15,12 @@
* GNU General Public License for more details.
*/
-import ch.cyberduck.core.cryptomator.CryptoVault;
import ch.cyberduck.core.dropbox.DropboxProtocol;
import ch.cyberduck.core.dropbox.DropboxSession;
import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DefaultX509TrustManager;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.VaultTest;
import org.junit.After;
@@ -38,11 +38,11 @@ public class AbstractDropboxTest extends VaultTest {
@Parameterized.Parameters(name = "vaultVersion = {0}")
public static Object[] data() {
- return new Object[]{CryptoVault.VAULT_VERSION_DEPRECATED, CryptoVault.VAULT_VERSION};
+ return new Object[]{VaultMetadata.Type.V8, VaultMetadata.Type.UVF};
}
@Parameterized.Parameter
- public int vaultVersion;
+ public VaultMetadata.Type vaultVersion;
@After
public void disconnect() throws Exception {
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
index 84f25f9340d..1060e833a55 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java
@@ -46,12 +46,14 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.CopyWorker;
import ch.cyberduck.core.worker.DeleteWorker;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomUtils;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -66,7 +68,6 @@
import com.dropbox.core.v2.files.Metadata;
import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -78,8 +79,8 @@ public void testCopyFile() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
final byte[] content = RandomUtils.nextBytes(40500);
@@ -104,8 +105,8 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
new CryptoTouchFeature<>(session, new DefaultTouchFeature(
@@ -121,16 +122,17 @@ public void testCopyToDifferentFolderCryptomator() throws Exception {
new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session);
}
+ //TODO
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
new CryptoTouchFeature<>(session, new DefaultTouchFeature(
@@ -152,8 +154,8 @@ public void testCopyFolder() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -187,8 +189,8 @@ public void testCopyFileIntoVault() throws Exception {
assertTrue(new DropboxFindFeature(session).find(cleartextFile));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -213,8 +215,8 @@ public void testCopyDirectoryIntoVault() throws Exception {
new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), cleartextFile, new TransferStatus());
assertTrue(new DropboxFindFeature(session).find(cleartextFolder));
assertTrue(new DropboxFindFeature(session).find(cleartextFile));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
// move directory into vault
@@ -238,8 +240,8 @@ public void testCopyFileOutsideVault() throws Exception {
new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), clearFolder, new TransferStatus());
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -264,8 +266,8 @@ public void testCopyDirectoryOutsideVault() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java
index ac5582d36aa..b91cc053ab9 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/CryptoDropboxSingleTransferWorkerTest.java
@@ -50,6 +50,7 @@
import ch.cyberduck.core.transfer.UploadTransfer;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.SingleTransferWorker;
import ch.cyberduck.test.IntegrationTest;
@@ -91,8 +92,8 @@ public void testUpload() throws Exception {
final OutputStream out2 = localFile2.getOutputStream(false);
IOUtils.write(content, out2);
out2.close();
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Transfer t = new UploadTransfer(new Host(new TestProtocol()), Collections.singletonList(new TransferItem(dir1, localDirectory1)), new NullFilter<>());
assertTrue(new SingleTransferWorker(session, session, t, new TransferOptions(), new TransferSpeedometer(t), new DisabledTransferPrompt() {
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java
index 3b6fa9869fd..f322ba2ef5d 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxDirectoryFeatureTest.java
@@ -35,8 +35,10 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -47,7 +49,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -57,8 +58,8 @@ public class DropboxDirectoryFeatureTest extends AbstractDropboxTest {
public void testMakeDirectoryEncrypted() throws Exception {
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final Path test = cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
@@ -69,13 +70,13 @@ public void testMakeDirectoryEncrypted() throws Exception {
}
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testMakeDirectoryLongFilenameEncrypted() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path test = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), test, new TransferStatus());
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java
index ef2ce97f56a..46d80a19ac0 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxListServiceTest.java
@@ -33,6 +33,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.junit.Test;
@@ -56,8 +57,8 @@ public class DropboxListServiceTest extends AbstractDropboxTest {
public void testListCryptomator() throws Exception {
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
assertTrue(new CryptoListService(session, new DropboxListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty());
final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java
index d40ecf497ab..d5efcce3615 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxMoveFeatureTest.java
@@ -39,6 +39,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.junit.Test;
@@ -64,8 +65,8 @@ public void testMove() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), folder, new TransferStatus());
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java
index 1c7bfd7cc38..99bf044b0e9 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxTouchFeatureTest.java
@@ -35,8 +35,10 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -49,19 +51,19 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
public class DropboxTouchFeatureTest extends AbstractDropboxTest {
+ //TODO
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testTouchLongFilenameEncrypted() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
@@ -74,12 +76,12 @@ public void testTouchLongFilenameEncrypted() throws Exception {
}
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testTouchLongFilenameEncryptedDefaultFeature() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final TransferStatus status = new TransferStatus();
final Path test = new CryptoTouchFeature<>(session, new DefaultTouchFeature(session), cryptomator).touch(
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java
index 13545619d4c..bb942fd195b 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/DropboxWriteFeatureTest.java
@@ -40,6 +40,7 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.test.IntegrationTest;
import org.apache.commons.lang3.RandomUtils;
@@ -72,8 +73,8 @@ public void testWrite() throws Exception {
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
final CryptoWriteFeature writer = new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator);
final FileHeader header = cryptomator.getFileHeaderCryptor().create();
diff --git a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
index 47f1208e211..e71563b8795 100644
--- a/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
+++ b/dropbox/src/test/java/ch/cyberduck/core/cryptomator/MoveWorkerTest.java
@@ -42,9 +42,11 @@
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.core.vault.DefaultVaultRegistry;
import ch.cyberduck.core.vault.VaultCredentials;
+import ch.cyberduck.core.vault.VaultMetadata;
import ch.cyberduck.core.worker.MoveWorker;
import ch.cyberduck.test.IntegrationTest;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
@@ -55,7 +57,6 @@
import java.util.EnumSet;
import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
@Category(IntegrationTest.class)
@RunWith(value = Parameterized.class)
@@ -67,8 +68,8 @@ public void testMoveSameFolderCryptomator() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path target = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus());
@@ -87,8 +88,8 @@ public void testMoveToDifferentFolderCryptomator() throws Exception {
final Path source = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus());
@@ -103,16 +104,17 @@ public void testMoveToDifferentFolderCryptomator() throws Exception {
cryptomator.getFeature(session, Delete.class, new DropboxDeleteFeature(session)).delete(Arrays.asList(target, targetFolder, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
}
+ //TODO
@Test
+ @Ignore(value = "Filename shortening not yet implemented")
public void testMoveToDifferentFolderLongFilenameCryptomator() throws Exception {
- assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED);
final Path home = new DefaultHomeFinderService(session).find();
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path source = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
final Path targetFolder = new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory));
final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
new CryptoTouchFeature<>(session, new DropboxTouchFeature(session), cryptomator).touch(
new CryptoWriteFeature<>(session, new DropboxWriteFeature(session), cryptomator), source, new TransferStatus());
@@ -133,8 +135,8 @@ public void testMoveFolder() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path folder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
cryptomator.getFeature(session, Write.class, new DropboxWriteFeature(session)), folder, new TransferStatus());
@@ -173,8 +175,8 @@ public void testMoveFileIntoVault() throws Exception {
assertTrue(new DefaultFindFeature(session).find(clearFile));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -199,8 +201,8 @@ public void testMoveDirectoryIntoVault() throws Exception {
new DropboxTouchFeature(session).touch(new DropboxWriteFeature(session), clearFile, new TransferStatus());
assertTrue(new DefaultFindFeature(session).find(clearFolder));
assertTrue(new DefaultFindFeature(session).find(clearFile));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
// move directory into vault
@@ -224,8 +226,8 @@ public void testMoveFileOutsideVault() throws Exception {
new DropboxDirectoryFeature(session).mkdir(new DropboxWriteFeature(session), clearFolder, new TransferStatus());
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
@@ -251,8 +253,8 @@ public void testMoveDirectoryOutsideVault() throws Exception {
final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
+ final AbstractVault cryptomator = new CryptoVaultProvider(session).create(session, null, new VaultCredentials("test"),
+ new VaultMetadata(vault, vaultVersion));
final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
session.withRegistry(registry);
cryptomator.getFeature(session, Directory.class, new DropboxDirectoryFeature(session)).mkdir(
diff --git a/eue/pom.xml b/eue/pom.xml
index c264b8473a0..81cdc95ea4e 100644
--- a/eue/pom.xml
+++ b/eue/pom.xml
@@ -19,7 +19,7 @@
parent
ch.cyberduck
- 9.3.0-SNAPSHOT
+ 9.3.0.uvfdraft.cryptolib-SNAPSHOT
eue
jar
diff --git a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java
deleted file mode 100644
index 883a1996c04..00000000000
--- a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueSingleUploadServiceTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package ch.cyberduck.core.cryptomator;
-
-/*
- * Copyright (c) 2002-2021 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.AbstractPath;
-import ch.cyberduck.core.AlphanumericRandomStringService;
-import ch.cyberduck.core.BytecountStreamListener;
-import ch.cyberduck.core.DisabledConnectionCallback;
-import ch.cyberduck.core.DisabledLoginCallback;
-import ch.cyberduck.core.DisabledPasswordCallback;
-import ch.cyberduck.core.DisabledProgressListener;
-import ch.cyberduck.core.Local;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.cryptomator.features.CryptoReadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoUploadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
-import ch.cyberduck.core.eue.AbstractEueSessionTest;
-import ch.cyberduck.core.eue.EueAttributesFinderFeature;
-import ch.cyberduck.core.eue.EueDeleteFeature;
-import ch.cyberduck.core.eue.EueDirectoryFeature;
-import ch.cyberduck.core.eue.EueFindFeature;
-import ch.cyberduck.core.eue.EueReadFeature;
-import ch.cyberduck.core.eue.EueResourceIdProvider;
-import ch.cyberduck.core.eue.EueSingleUploadService;
-import ch.cyberduck.core.eue.EueWriteFeature;
-import ch.cyberduck.core.features.AttributesFinder;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.io.BandwidthThrottle;
-import ch.cyberduck.core.io.StreamCopier;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.DefaultVaultRegistry;
-import ch.cyberduck.core.vault.VaultCredentials;
-import ch.cyberduck.test.IntegrationTest;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.RandomUtils;
-import org.cryptomator.cryptolib.api.FileHeader;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.UUID;
-
-import static org.junit.Assert.*;
-
-@Category(IntegrationTest.class)
-@RunWith(value = Parameterized.class)
-public class EueSingleUploadServiceTest extends AbstractEueSessionTest {
-
- @Test
- public void testUploadVault() throws Exception {
- final EueResourceIdProvider fileid = new EueResourceIdProvider(session);
- final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(AbstractPath.Type.directory)), new TransferStatus().setLength(0L));
- final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
- session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator));
- final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
- final byte[] content = RandomUtils.nextBytes(502400);
- IOUtils.write(content, local.getOutputStream(false));
- final TransferStatus writeStatus = new TransferStatus();
- final FileHeader header = cryptomator.getFileHeaderCryptor().create();
- writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header));
- writeStatus.setLength(content.length);
- final BytecountStreamListener count = new BytecountStreamListener();
- final CryptoUploadFeature feature = new CryptoUploadFeature<>(session,
- new EueSingleUploadService(session, fileid),
- cryptomator);
- feature.upload(new CryptoWriteFeature<>(session, new EueWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, null);
- assertEquals(content.length, count.getSent());
- assertTrue(writeStatus.isComplete());
- assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test));
- assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new EueAttributesFinderFeature(session, fileid)).find(test).getSize());
- final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length);
- final TransferStatus readStatus = new TransferStatus().setLength(content.length);
- final InputStream in = new CryptoReadFeature(session, new EueReadFeature(session, fileid), cryptomator).read(test, readStatus, new DisabledConnectionCallback());
- new StreamCopier(readStatus, readStatus).transfer(in, buffer);
- assertArrayEquals(content, buffer.toByteArray());
- cryptomator.getFeature(session, Delete.class, new EueDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
- local.delete();
- }
-}
diff --git a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java b/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java
deleted file mode 100644
index 9855b7e9cd1..00000000000
--- a/eue/src/test/java/ch/cyberduck/core/cryptomator/EueThresholdUploadServiceTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-package ch.cyberduck.core.cryptomator;
-
-/*
- * Copyright (c) 2002-2021 iterate GmbH. All rights reserved.
- * https://cyberduck.io/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-import ch.cyberduck.core.AlphanumericRandomStringService;
-import ch.cyberduck.core.BytecountStreamListener;
-import ch.cyberduck.core.DisabledConnectionCallback;
-import ch.cyberduck.core.DisabledLoginCallback;
-import ch.cyberduck.core.DisabledPasswordCallback;
-import ch.cyberduck.core.DisabledProgressListener;
-import ch.cyberduck.core.Local;
-import ch.cyberduck.core.Path;
-import ch.cyberduck.core.cryptomator.features.CryptoBulkFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoReadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoUploadFeature;
-import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature;
-import ch.cyberduck.core.eue.AbstractEueSessionTest;
-import ch.cyberduck.core.eue.EueAttributesFinderFeature;
-import ch.cyberduck.core.eue.EueDeleteFeature;
-import ch.cyberduck.core.eue.EueDirectoryFeature;
-import ch.cyberduck.core.eue.EueFindFeature;
-import ch.cyberduck.core.eue.EueReadFeature;
-import ch.cyberduck.core.eue.EueResourceIdProvider;
-import ch.cyberduck.core.eue.EueThresholdUploadService;
-import ch.cyberduck.core.eue.EueWriteFeature;
-import ch.cyberduck.core.features.AttributesFinder;
-import ch.cyberduck.core.features.Delete;
-import ch.cyberduck.core.features.Find;
-import ch.cyberduck.core.io.BandwidthThrottle;
-import ch.cyberduck.core.io.StreamCopier;
-import ch.cyberduck.core.shared.DisabledBulkFeature;
-import ch.cyberduck.core.transfer.Transfer;
-import ch.cyberduck.core.transfer.TransferItem;
-import ch.cyberduck.core.transfer.TransferStatus;
-import ch.cyberduck.core.vault.DefaultVaultRegistry;
-import ch.cyberduck.core.vault.VaultCredentials;
-import ch.cyberduck.test.IntegrationTest;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.RandomUtils;
-import org.cryptomator.cryptolib.api.FileHeader;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.UUID;
-
-import static org.junit.Assert.*;
-
-@Category(IntegrationTest.class)
-@RunWith(value = Parameterized.class)
-public class EueThresholdUploadServiceTest extends AbstractEueSessionTest {
-
- @Test
- public void testUploadVault() throws Exception {
- final EueResourceIdProvider fileid = new EueResourceIdProvider(session);
- final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L));
- final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
- final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
- session.withRegistry(registry);
- final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
- final byte[] content = RandomUtils.nextBytes(8840780);
- IOUtils.write(content, local.getOutputStream(false));
- final TransferStatus writeStatus = new TransferStatus();
- final FileHeader header = cryptomator.getFileHeaderCryptor().create();
- writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header));
- writeStatus.setLength(content.length);
- final BytecountStreamListener count = new BytecountStreamListener();
- final CryptoUploadFeature feature = new CryptoUploadFeature<>(session,
- new EueThresholdUploadService(session, fileid, registry),
- cryptomator);
- feature.upload(new CryptoWriteFeature<>(session, new EueWriteFeature(session, fileid), cryptomator), test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED), new DisabledProgressListener(), count, writeStatus, new DisabledConnectionCallback());
- assertEquals(content.length, count.getSent());
- assertTrue(writeStatus.isComplete());
- assertTrue(cryptomator.getFeature(session, Find.class, new EueFindFeature(session, fileid)).find(test));
- assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new EueAttributesFinderFeature(session, fileid)).find(test).getSize());
- final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length);
- final TransferStatus readStatus = new TransferStatus().setLength(content.length);
- final InputStream in = new CryptoReadFeature(session, new EueReadFeature(session, fileid), cryptomator).read(test, readStatus, new DisabledConnectionCallback());
- new StreamCopier(readStatus, readStatus).transfer(in, buffer);
- assertArrayEquals(content, buffer.toByteArray());
- cryptomator.getFeature(session, Delete.class, new EueDeleteFeature(session, fileid)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback());
- local.delete();
- }
-
- @Test
- public void testUploadVaultWithBulkFeature() throws Exception {
- final EueResourceIdProvider fileid = new EueResourceIdProvider(session);
- final Path container = new EueDirectoryFeature(session, fileid).mkdir(new EueWriteFeature(session, fileid), new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus().setLength(0L));
- final Path vault = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
- final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
- final CryptoVault cryptomator = new CryptoVault(vault);
- cryptomator.create(session, new VaultCredentials("test"), vaultVersion);
- final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator);
- session.withRegistry(registry);
- final Local local = new Local(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString());
- final byte[] content = RandomUtils.nextBytes(50240000);
- IOUtils.write(content, local.getOutputStream(false));
- final TransferStatus writeStatus = new TransferStatus();
- final FileHeader header = cryptomator.getFileHeaderCryptor().create();
- writeStatus.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header));
- writeStatus.setLength(content.length);
- final CryptoBulkFeature