From 9d5b323efa152dd76eb6524b9219b2f0027f493a Mon Sep 17 00:00:00 2001 From: chenkins Date: Fri, 6 Dec 2024 16:43:55 +0100 Subject: [PATCH 01/15] First ideas for uvf imple. --- .../core/cryptomator/CryptoVault.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 42535c125eb..2fe2b9d9053 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -76,6 +76,10 @@ /** * Cryptomator vault implementation */ +// UVF: Keep this as façade for detecting vault version and delegating to implementation +// - upon create, the vault version is determined from preferences -> set the delegate impl +// - upon unlock, the vault version needs to be determined by reading masterkey.cryptomator or (!) vault.uvf file -> set the delegate impl +// - open is called either from create or unlock, hence at this point we can delegate calls to the v6/v7/uvf imple? public class CryptoVault implements Vault { private static final Logger log = LogManager.getLogger(CryptoVault.class); @@ -114,6 +118,7 @@ public class CryptoVault implements Vault { private final byte[] pepper; public CryptoVault(final Path home) { + // UVF: readVaultConfig - do we need to try multiple file names for dection "masterkey.cryptomator" and "vault.uvf"? this(home, DefaultVaultRegistry.DEFAULT_MASTERKEY_FILE_NAME, DEFAULT_VAULTCONFIG_FILE_NAME, VAULT_PEPPER); } @@ -121,6 +126,8 @@ public CryptoVault(final Path home, final String masterkey, final String config, 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)); + + // UVF: no pepper for uvf this.pepper = pepper; // New vault home with vault flag set for internal use final EnumSet type = EnumSet.copyOf(home.getType()); @@ -133,10 +140,13 @@ public CryptoVault(final Path home, final String masterkey, final String config, } } + // UVF: VaultCredentials must come with specification of recipient, see the recipient header in https://github.com/encryption-alliance/unified-vault-format/tree/develop/vault%20metadata#example-per-recipient-unprotected-header + // UVF: version string instead of int? public synchronized Path create(final Session session, final VaultCredentials credentials, final int version) throws BackgroundException { return this.create(session, null, credentials, version); } + // UVF: Switch on version -> CryptoVaultImple: one for v6/v7 and one for uvf 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()) { @@ -219,6 +229,7 @@ public synchronized CryptoVault load(final Session session, final PasswordCal return this.unlock(session, prompt, bookmark, passphrase); } + // UVF: VaultConfig v6/v7 only private VaultConfig readVaultConfig(final Session session) throws BackgroundException { try { final String token = new ContentReader(session).read(config); @@ -235,7 +246,7 @@ private VaultConfig readVaultConfig(final Session session) throws BackgroundE } } - + // UVF: v6/v7 specific public static VaultConfig parseVaultConfigFromJWT(final String token) { final DecodedJWT decoded = JWT.decode(token); return new VaultConfig( @@ -245,6 +256,8 @@ public static VaultConfig parseVaultConfigFromJWT(final String token) { decoded.getAlgorithm(), decoded); } + // UVF: v6/v7 and vault.uvf are different - can we use the new MasterKey interface from https://github.com/cryptomator/cryptolib/pull/51/files? + // called from readVaultConfig() only which is v6/v7 only... good for us! private MasterkeyFile readMasterkeyFile(final Session session, final Path masterkey) throws BackgroundException { log.debug("Read master key {}", masterkey); try (Reader reader = new ContentReader(session).getReader(masterkey)) { @@ -256,6 +269,7 @@ private MasterkeyFile readMasterkeyFile(final Session session, final Path mas } public CryptoVault unlock(final Session session, final PasswordCallback prompt, final Host bookmark, final String passphrase) throws BackgroundException { + // UVF: we need to detect the version here, vault.uvf is different from VaultConfig 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()) @@ -263,6 +277,7 @@ public CryptoVault unlock(final Session session, final PasswordCallback promp return this; } + // UVF: extract to v6/v7 and uvf imple public void unlock(final VaultConfig vaultConfig, final String passphrase, final Host bookmark, final PasswordCallback prompt, final String message) throws BackgroundException { final Credentials credentials; @@ -316,6 +331,7 @@ public synchronized void close() { fileNameCryptor = null; } + // UVF: at this point, we have done the version detection, we can directly go to a delegate, no switch protected CryptoFilename createFilenameProvider(final VaultConfig vaultConfig) { switch(vaultConfig.version) { case VAULT_VERSION_DEPRECATED: @@ -334,10 +350,15 @@ protected CryptoDirectory createDirectoryProvider(final VaultConfig vaultConfig) } } + // UVF: extract to v6/v7/uvf imple, VaultConfig only for v6/v7 + // pro memoria: + // create -> open + // unlock -> open protected void open(final VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException { this.open(vaultConfig, passphrase, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig)); } + // UVF: extract to v6/v7/uvf, at this point we know which version protected void open(final VaultConfig vaultConfig, final CharSequence passphrase, final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException { try { @@ -352,10 +373,12 @@ protected void open(final VaultConfig vaultConfig, final CharSequence passphrase } } + // UVF: unused?! protected void open(final VaultConfig vaultConfig, final Masterkey masterKey) throws BackgroundException { this.open(vaultConfig, masterKey, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig)); } + // UVF: extract to v6/v7 imple, can we use the new MasterKey interface from https://github.com/cryptomator/cryptolib/pull/51/files? protected void open(final VaultConfig vaultConfig, final Masterkey masterKey, final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException { this.vaultVersion = vaultConfig.version; @@ -403,6 +426,7 @@ public Path encrypt(final Session session, final Path file, boolean metadata) return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); } + // UVF: extract to delegate? public Path encrypt(final Session session, final Path file, final String directoryId, boolean metadata) throws BackgroundException { final Path encrypted; if(file.isFile() || metadata) { From 719c5ed87c80a3d56597b1952b8bd60ab12470b6 Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Mon, 6 Jan 2025 10:55:54 +0200 Subject: [PATCH 02/15] Switch to uvf draft snapshot dependency. --- cryptomator/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cryptomator/pom.xml b/cryptomator/pom.xml index 8875a0ac066..0b5a3fe4a80 100644 --- a/cryptomator/pom.xml +++ b/cryptomator/pom.xml @@ -25,7 +25,7 @@ jar - 2.1.2.1 + 2.3.0-uvfdraft-SNAPSHOT From 8b5a6ce271947c8d16ba7251eaa576556f2262d6 Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Sat, 18 Jan 2025 11:17:30 +0100 Subject: [PATCH 03/15] Migrate API changes. --- .../ch/cyberduck/core/cryptomator/CryptoVault.java | 11 ++++++----- .../cyberduck/core/cryptomator/CryptoVaultTest.java | 4 ++-- .../SFTPCryptomatorInteroperabilityTest.java | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 2fe2b9d9053..89972f09b47 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -45,6 +45,7 @@ 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; @@ -160,7 +161,7 @@ public synchronized Path create(final Session session, final String region, f } final String passphrase = credentials.getPassword(); final ByteArrayOutputStream mkArray = new ByteArrayOutputStream(); - final Masterkey mk = Masterkey.generate(FastSecureRandomProvider.get().provide()); + final PerpetualMasterkey mk = Masterkey.generate(FastSecureRandomProvider.get().provide()); final MasterkeyFileAccess access = new MasterkeyFileAccess(pepper, FastSecureRandomProvider.get().provide()); final MasterkeyFile masterkeyFile; try { @@ -362,7 +363,7 @@ protected void open(final VaultConfig vaultConfig, final CharSequence passphrase 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); + final PerpetualMasterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase); this.open(vaultConfig, masterKey, filenameProvider, directoryProvider); } catch(IllegalArgumentException | IOException e) { @@ -374,12 +375,12 @@ protected void open(final VaultConfig vaultConfig, final CharSequence passphrase } // UVF: unused?! - protected void open(final VaultConfig vaultConfig, final Masterkey masterKey) throws BackgroundException { + protected void open(final VaultConfig vaultConfig, final PerpetualMasterkey masterKey) throws BackgroundException { this.open(vaultConfig, masterKey, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig)); } // UVF: extract to v6/v7 imple, can we use the new MasterKey interface from https://github.com/cryptomator/cryptolib/pull/51/files? - protected void open(final VaultConfig vaultConfig, final Masterkey masterKey, + protected void open(final VaultConfig vaultConfig, final PerpetualMasterkey masterKey, final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException { this.vaultVersion = vaultConfig.version; final CryptorProvider provider = CryptorProvider.forScheme(vaultConfig.getCipherCombo()); @@ -392,7 +393,7 @@ protected void open(final VaultConfig vaultConfig, final Masterkey masterKey, this.nonceSize = vaultConfig.getNonceSize(); } - private Masterkey getMasterKey(final MasterkeyFile mkFile, final CharSequence passphrase) throws IOException { + 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( diff --git a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java index 4bcfc9c8d12..59f21686881 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/CryptoVaultTest.java @@ -41,7 +41,7 @@ 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; @@ -563,7 +563,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/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPCryptomatorInteroperabilityTest.java b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPCryptomatorInteroperabilityTest.java index 0dac54678ba..58a14ea9a7d 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPCryptomatorInteroperabilityTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPCryptomatorInteroperabilityTest.java @@ -54,6 +54,7 @@ import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.MasterkeyLoader; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; +import org.cryptomator.cryptolib.api.PerpetualMasterkey; import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.cryptomator.cryptolib.common.ReseedingSecureRandom; import org.junit.After; @@ -99,7 +100,7 @@ public void startSerer() throws Exception { default: csprng = FastSecureRandomProvider.get().provide(); } - final Masterkey mk = Masterkey.generate(csprng); + final PerpetualMasterkey mk = Masterkey.generate(csprng); final MasterkeyFileAccess mkAccess = new MasterkeyFileAccess(CryptoVault.VAULT_PEPPER, csprng); final java.nio.file.Path mkPath = Paths.get(vault.toString(), DefaultVaultRegistry.DEFAULT_MASTERKEY_FILE_NAME); mkAccess.persist(mk, mkPath, passphrase); From b68915332e4baad82348cf4446f68659fdfb94a9 Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Sat, 18 Jan 2025 19:42:34 +0100 Subject: [PATCH 04/15] Extract filename cryptor and provider. --- .../core/cryptomator/CryptoVault.java | 29 +--- .../cyberduck/core/cryptomator/UVFVault.java | 136 ++++++++++++++++++ .../impl/CryptoDirectoryV6Provider.java | 19 +-- .../impl/CryptoDirectoryV7Provider.java | 21 +-- .../impl/CryptoDirectoryV6ProviderTest.java | 74 +++------- .../impl/CryptoDirectoryV7ProviderTest.java | 78 +++------- 6 files changed, 201 insertions(+), 156 deletions(-) create mode 100644 cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 89972f09b47..595c3629e7c 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -342,29 +342,20 @@ protected CryptoFilename createFilenameProvider(final VaultConfig vaultConfig) { } } - protected CryptoDirectory createDirectoryProvider(final VaultConfig vaultConfig) { + protected CryptoDirectory createDirectoryProvider(final VaultConfig vaultConfig, final CryptoFilename filenameProvider, final CryptorCache filenameCryptor) { switch(vaultConfig.version) { case VAULT_VERSION_DEPRECATED: - return new CryptoDirectoryV6Provider(vault, this); + return new CryptoDirectoryV6Provider(vault, filenameProvider, filenameCryptor); default: - return new CryptoDirectoryV7Provider(vault, this); + return new CryptoDirectoryV7Provider(vault, filenameProvider, filenameCryptor); } } - // UVF: extract to v6/v7/uvf imple, VaultConfig only for v6/v7 - // pro memoria: - // create -> open - // unlock -> open - protected void open(final VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException { - this.open(vaultConfig, passphrase, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig)); - } - // UVF: extract to v6/v7/uvf, at this point we know which version - protected void open(final VaultConfig vaultConfig, final CharSequence passphrase, - final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException { + protected void open(final VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException { try { final PerpetualMasterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase); - this.open(vaultConfig, masterKey, filenameProvider, directoryProvider); + this.open(vaultConfig, masterKey); } catch(IllegalArgumentException | IOException e) { throw new VaultException("Failure reading key file", e); @@ -376,20 +367,14 @@ protected void open(final VaultConfig vaultConfig, final CharSequence passphrase // UVF: unused?! protected void open(final VaultConfig vaultConfig, final PerpetualMasterkey masterKey) throws BackgroundException { - this.open(vaultConfig, masterKey, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig)); - } - - // UVF: extract to v6/v7 imple, can we use the new MasterKey interface from https://github.com/cryptomator/cryptolib/pull/51/files? - protected void open(final VaultConfig vaultConfig, final PerpetualMasterkey 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.filenameProvider = this.createFilenameProvider(vaultConfig); + this.directoryProvider = this.createDirectoryProvider(vaultConfig, this.filenameProvider, this.fileNameCryptor); this.nonceSize = vaultConfig.getNonceSize(); } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java new file mode 100644 index 00000000000..6f7803728d6 --- /dev/null +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -0,0 +1,136 @@ +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.PasswordCallback; +import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; +import ch.cyberduck.core.Session; +import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; +import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider; +import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.features.Vault; +import ch.cyberduck.core.vault.VaultCredentials; + +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.UVFMasterkey; + +import java.util.EnumSet; + +public class UVFVault implements Vault { + + private static final Logger log = LogManager.getLogger(UVFVault.class); + + /** + * Root of vault directory + */ + private final Path home; + private final Path vault; + + private final String decrypted; + private Cryptor cryptor; + private CryptorCache fileNameCryptor; + private CryptoFilename filenameProvider; + private CryptoDirectory directoryProvider; + + public UVFVault(final Path home, final String decryptedPayload) { + this.home = home; + this.decrypted = decryptedPayload; + // 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())); + } + } + + @Override + public Path create(final Session session, final String region, final VaultCredentials credentials) throws BackgroundException { + return null; + } + + // load -> unlock -> open + @Override + public UVFVault load(final Session session, final PasswordCallback prompt) throws BackgroundException { + UVFMasterkey masterKey = UVFMasterkey.fromDecryptedPayload(this.decrypted); + + final CryptorProvider provider = CryptorProvider.forScheme(CryptorProvider.Scheme.UVF_DRAFT); + log.debug("Initialized crypto provider {}", provider); + this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide()); + this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor()); + this.filenameProvider = new CryptoFilenameV7Provider(/* TODO threshold was previously defined in vault.config - default now? */); + this.directoryProvider = new CryptoDirectoryV7Provider(vault, filenameProvider, fileNameCryptor); + //TODO where? this.nonceSize = vaultConfig.getNonceSize(); + return this; + } + + @Override + public void close() { + + } + + @Override + public boolean contains(final Path file) { + return false; + } + + @Override + public Path encrypt(final Session session, final Path file) throws BackgroundException { + return null; + } + + @Override + public Path encrypt(final Session session, final Path file, final boolean metadata) throws BackgroundException { + return null; + } + + @Override + public Path decrypt(final Session session, final Path file) throws BackgroundException { + return null; + } + + @Override + public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) { + return 0; + } + + @Override + public long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws BackgroundException { + return 0; + } + + @Override + public T getFeature(final Session session, final Class type, final T delegate) { + return null; + } + + @Override + public State getState() { + return null; + } + + @Override + public Path getHome() { + return null; + } +} 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 index 8efb36c5f69..9ad0728a9c4 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.cache.LRUCache; import ch.cyberduck.core.cryptomator.ContentReader; import ch.cyberduck.core.cryptomator.CryptoDirectory; +import ch.cyberduck.core.cryptomator.CryptoFilename; import ch.cyberduck.core.cryptomator.CryptoVault; import ch.cyberduck.core.cryptomator.CryptorCache; import ch.cyberduck.core.exception.BackgroundException; @@ -48,28 +49,30 @@ public class CryptoDirectoryV6Provider implements CryptoDirectory { private final Path dataRoot; private final Path home; - private final CryptoVault cryptomator; + private final CryptoFilename filenameProvider; + private final CryptorCache filenameCryptor; private final RandomStringService random - = new UUIDRandomStringService(); + = new UUIDRandomStringService(); private final Lock lock = new ReentrantLock(); private final LRUCache, String> cache = LRUCache.build( - PreferencesFactory.get().getInteger("cryptomator.cache.size")); + PreferencesFactory.get().getInteger("cryptomator.cache.size")); - public CryptoDirectoryV6Provider(final Path vault, final CryptoVault cryptomator) { + public CryptoDirectoryV6Provider(final Path vault, final CryptoFilename filenameProvider, final CryptorCache filenameCryptor) { this.home = vault; this.dataRoot = new Path(vault, DATA_DIR_NAME, vault.getType()); - this.cryptomator = cryptomator; + this.filenameProvider = filenameProvider; + this.filenameCryptor = filenameCryptor; } @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)); + final String ciphertextName = prefix + filenameCryptor.encryptFilename(CryptorCache.BASE32, filename, directoryId.getBytes(StandardCharsets.UTF_8)); log.debug("Encrypted filename {} to {}", filename, ciphertextName); - return cryptomator.getFilenameProvider().deflate(session, ciphertextName); + return filenameProvider.deflate(session, ciphertextName); } @Override @@ -87,7 +90,7 @@ public Path toEncrypted(final Session session, final String directoryId, fina log.debug("Use directory ID '{}' for folder {}", id, directory); attributes.setDirectoryId(id); attributes.setDecrypted(directory); - final String directoryIdHash = cryptomator.getFileNameCryptor().hashDirectoryId(id); + final String directoryIdHash = filenameCryptor.hashDirectoryId(id); // Intermediate directory final Path intermediate = new Path(dataRoot, directoryIdHash.substring(0, 2), dataRoot.getType()); // Add encrypted type 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 index 28b4bb6211d..cfb9a8b55e2 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java @@ -20,7 +20,8 @@ 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.cryptomator.CryptoFilename; +import ch.cyberduck.core.cryptomator.CryptorCache; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; @@ -41,22 +42,24 @@ public class CryptoDirectoryV7Provider extends CryptoDirectoryV6Provider { 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 CryptoFilename filenameProvider; + private final CryptorCache filenameCryptor; private final RandomStringService random - = new UUIDRandomStringService(); + = new UUIDRandomStringService(); - public CryptoDirectoryV7Provider(final Path vault, final CryptoVault cryptomator) { - super(vault, cryptomator); - this.cryptomator = cryptomator; + public CryptoDirectoryV7Provider(final Path vault, final CryptoFilename filenameProvider, final CryptorCache filenameCryptor) { + super(vault, filenameProvider, filenameCryptor); + this.filenameProvider = filenameProvider; + this.filenameCryptor = filenameCryptor; } @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; + final String ciphertextName = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), + filename, directoryId.getBytes(StandardCharsets.UTF_8)) + EXTENSION_REGULAR; log.debug("Encrypted filename {} to {}", filename, ciphertextName); - return cryptomator.getFilenameProvider().deflate(session, ciphertextName); + return filenameProvider.deflate(session, ciphertextName); } protected String load(final Session session, final Path directory) throws BackgroundException { 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 index a2f78fa324e..f4b09439953 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6ProviderTest.java @@ -15,27 +15,20 @@ * 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.cryptomator.CryptorCache; 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.cryptomator.cryptolib.api.Cryptor; +import org.cryptomator.cryptolib.api.CryptorProvider; +import org.cryptomator.cryptolib.api.PerpetualMasterkey; import org.junit.Test; -import java.io.InputStream; -import java.nio.charset.Charset; +import java.security.SecureRandom; import java.util.EnumSet; import static org.junit.Assert.assertEquals; @@ -46,62 +39,31 @@ 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); + final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_CTRMAC); + final SecureRandom random = new SecureRandom(); + final Cryptor cryptor = crypto.provide(PerpetualMasterkey.generate(random), random); + final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, new CryptoFilenameV6Provider(home), new CryptorCache(cryptor.fileNameCryptor())); 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); + final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_CTRMAC); + final SecureRandom random = new SecureRandom(); + final Cryptor cryptor = crypto.provide(PerpetualMasterkey.generate(random), random); + final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, new CryptoFilenameV6Provider(home), new CryptorCache(cryptor.fileNameCryptor())); 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); + final NullSession session = new NullSession(new Host(new TestProtocol())); + final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_CTRMAC); + final SecureRandom csprng = new SecureRandom(); + final Cryptor cryptor = crypto.provide(PerpetualMasterkey.generate(csprng), csprng); + final CryptoDirectory provider = new CryptoDirectoryV6Provider(home, new CryptoFilenameV6Provider(home), new CryptorCache(cryptor.fileNameCryptor())); assertNotNull(provider.toEncrypted(session, null, home)); final Path f = new Path("/vault/f", EnumSet.of(Path.Type.directory)); assertNotNull(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/CryptoDirectoryV7ProviderTest.java index ee372bed4d0..7ad14d49335 100644 --- a/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java +++ b/cryptomator/src/test/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7ProviderTest.java @@ -15,32 +15,22 @@ * 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.cryptomator.CryptorCache; 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.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.api.CryptorProvider; +import org.cryptomator.cryptolib.api.PerpetualMasterkey; import org.junit.Test; -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; @@ -49,65 +39,31 @@ public class CryptoDirectoryV7ProviderTest { @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); + final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_GCM); + final SecureRandom random = new SecureRandom(); + final Cryptor cryptor = crypto.provide(PerpetualMasterkey.generate(random), random); + final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, new CryptoFilenameV7Provider(), new CryptorCache(cryptor.fileNameCryptor())); 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 CryptoDirectoryV7Provider(home, vault); + final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_GCM); + final SecureRandom random = new SecureRandom(); + final Cryptor cryptor = crypto.provide(PerpetualMasterkey.generate(random), random); + final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, new CryptoFilenameV7Provider(), new CryptorCache(cryptor.fileNameCryptor())); 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\": 8\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()); - } - 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 CryptoDirectoryV7Provider(home, vault); + final NullSession session = new NullSession(new Host(new TestProtocol())); + final CryptorProvider crypto = CryptorProvider.forScheme(CryptorProvider.Scheme.SIV_GCM); + final SecureRandom random = new SecureRandom(); + final Cryptor cryptor = crypto.provide(PerpetualMasterkey.generate(random), random); + final CryptoDirectory provider = new CryptoDirectoryV7Provider(home, new CryptoFilenameV7Provider(), new CryptorCache(cryptor.fileNameCryptor())); assertNotNull(provider.toEncrypted(session, null, home)); final Path f = new Path("/vault/f", EnumSet.of(Path.Type.directory)); assertNotNull(provider.toEncrypted(session, null, f)); From 3e90a8f3e755ee8c210ba279440c6492485708aa Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Mon, 20 Jan 2025 09:21:57 +0100 Subject: [PATCH 05/15] Intermediate interface for Cryptomator stuff. --- .../cryptomator/CryptoTransferStatus.java | 4 +- .../core/cryptomator/CryptoVault.java | 21 ++++--- .../cryptomator/CryptoVaultInterface.java | 59 +++++++++++++++++++ .../features/CryptoBulkFeature.java | 6 +- .../features/CryptoChecksumCompute.java | 8 +-- .../features/CryptoCopyFeature.java | 6 +- .../features/CryptoDeleteV6Feature.java | 6 +- .../features/CryptoDeleteV7Feature.java | 6 +- .../features/CryptoDirectoryV6Feature.java | 6 +- .../features/CryptoDirectoryV7Feature.java | 6 +- .../features/CryptoDownloadFeature.java | 7 +-- .../features/CryptoEncryptionFeature.java | 3 +- .../features/CryptoMoveV6Feature.java | 6 +- .../features/CryptoMoveV7Feature.java | 6 +- .../features/CryptoMultipartWriteFeature.java | 6 +- .../features/CryptoReadFeature.java | 6 +- .../features/CryptoTimestampFeature.java | 6 +- .../features/CryptoTouchFeature.java | 6 +- .../features/CryptoUploadFeature.java | 6 +- .../features/CryptoWriteFeature.java | 6 +- 20 files changed, 121 insertions(+), 65 deletions(-) create mode 100644 cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java 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 fc5e06559f8..34da1e17f5b 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 CryptoVaultInterface vault; - public CryptoTransferStatus(final CryptoVault vault, final TransferStatus proxy) { + public CryptoTransferStatus(final CryptoVaultInterface vault, final TransferStatus proxy) { super(proxy); this.vault = vault; this.withLength(vault.toCiphertextSize(proxy.getOffset(), proxy.getLength())) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 595c3629e7c..155574a72bb 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -81,7 +81,7 @@ // - upon create, the vault version is determined from preferences -> set the delegate impl // - upon unlock, the vault version needs to be determined by reading masterkey.cryptomator or (!) vault.uvf file -> set the delegate impl // - open is called either from create or unlock, hence at this point we can delegate calls to the v6/v7/uvf imple? -public class CryptoVault implements Vault { +public class CryptoVault implements CryptoVaultInterface { private static final Logger log = LogManager.getLogger(CryptoVault.class); public static final int VAULT_VERSION_DEPRECATED = 6; @@ -402,16 +402,6 @@ public boolean contains(final Path file) { return false; } - @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); - } - // UVF: extract to delegate? public Path encrypt(final Session session, final Path file, final String directoryId, boolean metadata) throws BackgroundException { final Path encrypted; @@ -594,38 +584,47 @@ public Path getHome() { return home; } + @Override public Path 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 CryptorCache getFileNameCryptor() { return fileNameCryptor; } + @Override public CryptoFilename getFilenameProvider() { return filenameProvider; } + @Override public CryptoDirectory getDirectoryProvider() { return directoryProvider; } + @Override public int getNonceSize() { return nonceSize; } + @Override public int numberOfChunks(final long cleartextFileSize) { return (int) (cleartextFileSize / cryptor.fileContentCryptor().cleartextChunkSize() + ((cleartextFileSize % cryptor.fileContentCryptor().cleartextChunkSize() > 0) ? 1 : 0)); diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java new file mode 100644 index 00000000000..83116b1fae6 --- /dev/null +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java @@ -0,0 +1,59 @@ +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.Path; +import ch.cyberduck.core.Session; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.features.Vault; + +import org.cryptomator.cryptolib.api.FileContentCryptor; +import org.cryptomator.cryptolib.api.FileHeaderCryptor; + +public interface CryptoVaultInterface extends Vault { + + Path getMasterkey(); + + Path getConfig(); + + FileHeaderCryptor getFileHeaderCryptor(); + + FileContentCryptor getFileContentCryptor(); + + CryptorCache getFileNameCryptor(); + + CryptoFilename getFilenameProvider(); + + CryptoDirectory getDirectoryProvider(); + + int getNonceSize(); + + int numberOfChunks(long cleartextFileSize); + + long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws CryptoInvalidFilesizeException; + + @Override + default Path encrypt(Session session, Path file) throws BackgroundException { + return this.encrypt(session, file, file.attributes().getDirectoryId(), false); + } + + @Override + default Path encrypt(Session session, Path file, boolean metadata) throws BackgroundException { + return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); + } + + Path encrypt(Session session, Path file, String directoryId, boolean metadata) throws BackgroundException; +} 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 c16210fc644..8c866c954d6 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.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -46,9 +46,9 @@ public class CryptoBulkFeature implements Bulk { private final Session session; private final Bulk delegate; - private final CryptoVault cryptomator; + private final CryptoVaultInterface cryptomator; - public CryptoBulkFeature(final Session session, final Bulk delegate, final Delete delete, final CryptoVault cryptomator) { + public CryptoBulkFeature(final Session session, final Bulk delegate, final Delete delete, final CryptoVaultInterface cryptomator) { this.session = session; this.delegate = delegate.withDelete(cryptomator.getFeature(session, Delete.class, delete)); this.cryptomator = cryptomator; 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 51d4dc0c635..00c98ee147b 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 @@ -16,7 +16,7 @@ */ import ch.cyberduck.core.cryptomator.CryptoOutputStream; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.CryptoVaultInterface; 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 CryptoVaultInterface cryptomator; private final ChecksumCompute delegate; - public CryptoChecksumCompute(final ChecksumCompute delegate, final CryptoVault vault) { - this.cryptomator = vault; + public CryptoChecksumCompute(final ChecksumCompute delegate, final CryptoVaultInterface 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 06e77f3d2d5..a4d37372387 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.CryptoVaultInterface; 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 CryptoVaultInterface vault; private Session target; - public CryptoCopyFeature(final Session session, final Copy proxy, final CryptoVault vault) { + public CryptoCopyFeature(final Session session, final Copy proxy, final CryptoVaultInterface 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 index aa20ce52cff..38862d62f57 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java @@ -21,7 +21,7 @@ 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.cryptomator.CryptoVaultInterface; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; @@ -44,10 +44,10 @@ public class CryptoDeleteV6Feature implements Delete, Trash { private final Session session; private final Delete proxy; - private final CryptoVault vault; + private final CryptoVaultInterface vault; private final CryptoFilename filenameProvider; - public CryptoDeleteV6Feature(final Session session, final Delete proxy, final CryptoVault vault) { + public CryptoDeleteV6Feature(final Session session, final Delete proxy, final CryptoVaultInterface vault) { this.session = session; this.proxy = proxy; this.vault = vault; 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 611988cb485..da7ad1b78e1 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 @@ -22,7 +22,7 @@ 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.cryptomator.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; @@ -46,10 +46,10 @@ public class CryptoDeleteV7Feature implements Delete, Trash { private final Session session; private final Delete proxy; - private final CryptoVault vault; + private final CryptoVaultInterface 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 CryptoVaultInterface vault) { this.session = session; this.proxy = proxy; this.vault = vault; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java index 5efb75be384..bfebd38ca7c 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java @@ -20,7 +20,7 @@ import ch.cyberduck.core.Session; import ch.cyberduck.core.UUIDRandomStringService; import ch.cyberduck.core.cryptomator.ContentWriter; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; @@ -40,11 +40,11 @@ public class CryptoDirectoryV6Feature implements Directory { private final Session session; private final Write writer; private final Directory delegate; - private final CryptoVault vault; + private final CryptoVaultInterface vault; private final RandomStringService random = new UUIDRandomStringService(); public CryptoDirectoryV6Feature(final Session session, final Directory delegate, - final Write writer, final CryptoVault cryptomator) { + final Write writer, final CryptoVaultInterface cryptomator) { this.session = session; this.writer = writer; this.delegate = delegate; 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 17547e2793c..aad32a4612f 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 @@ -20,7 +20,7 @@ import ch.cyberduck.core.Session; import ch.cyberduck.core.UUIDRandomStringService; import ch.cyberduck.core.cryptomator.ContentWriter; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -42,11 +42,11 @@ public class CryptoDirectoryV7Feature implements Directory { private final Session session; private final Write writer; private final Directory delegate; - private final CryptoVault vault; + private final CryptoVaultInterface vault; private final RandomStringService random = new UUIDRandomStringService(); public CryptoDirectoryV7Feature(final Session session, final Directory delegate, - final Write writer, final CryptoVault cryptomator) { + final Write writer, final CryptoVaultInterface cryptomator) { this.session = session; this.writer = writer; this.delegate = delegate; 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 dc803e4c911..20386818dba 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.CryptoVaultInterface; 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 CryptoVaultInterface vault; - public CryptoDownloadFeature(final Session session, final Download proxy, final Read reader, final CryptoVault vault) { + public CryptoDownloadFeature(final Session session, final Download proxy, final Read reader, final CryptoVaultInterface vault) { this.session = session; this.proxy = proxy.withReader(new CryptoReadFeature(session, reader, vault)); 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 index e09bdb213c5..bbbb60a0d86 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java @@ -19,7 +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.CryptoVaultInterface; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; import ch.cyberduck.core.features.Delete; @@ -34,9 +34,9 @@ public class CryptoMoveV6Feature implements Move { private final Session session; private final Move proxy; - private final CryptoVault vault; + private final CryptoVaultInterface vault; - public CryptoMoveV6Feature(final Session session, final Move delegate, final CryptoVault cryptomator) { + public CryptoMoveV6Feature(final Session session, final Move delegate, final CryptoVaultInterface cryptomator) { this.session = session; this.proxy = delegate; this.vault = cryptomator; 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 ee0280dfc67..6c3ab33c352 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,7 +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.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; @@ -35,9 +35,9 @@ public class CryptoMoveV7Feature implements Move { private final Session session; private final Move proxy; - private final CryptoVault vault; + private final CryptoVaultInterface vault; - public CryptoMoveV7Feature(final Session session, final Move delegate, final CryptoVault cryptomator) { + public CryptoMoveV7Feature(final Session session, final Move delegate, final CryptoVaultInterface cryptomator) { this.session = session; this.proxy = delegate; this.vault = cryptomator; 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..f3f9f14ea2c 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.CryptoVaultInterface; 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 CryptoVaultInterface 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 CryptoVaultInterface 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 ebfb913d119..792201d7adf 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 @@ -20,7 +20,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.cryptomator.CryptoInputStream; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.CryptoVaultInterface; 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 CryptoVaultInterface vault; - public CryptoReadFeature(final Session session, final Read proxy, final CryptoVault vault) { + public CryptoReadFeature(final Session session, final Read proxy, final CryptoVaultInterface 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..70506559f95 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 @@ -18,7 +18,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; import ch.cyberduck.core.cryptomator.CryptoTransferStatus; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.CryptoVaultInterface; 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 CryptoVaultInterface vault; - public CryptoTimestampFeature(final Session session, final Timestamp proxy, final CryptoVault vault) { + public CryptoTimestampFeature(final Session session, final Timestamp proxy, final CryptoVaultInterface 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 4fe570196a1..793a8c1d7cf 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.CryptoVaultInterface; 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 CryptoVaultInterface vault; - public CryptoTouchFeature(final Session session, final Touch proxy, final Write writer, final CryptoVault cryptomator) { + public CryptoTouchFeature(final Session session, final Touch proxy, final Write writer, final CryptoVaultInterface cryptomator) { this.session = session; this.proxy = proxy.withWriter(new CryptoWriteFeature<>(session, writer, cryptomator)); 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 4c677786be3..7beecee0570 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 @@ -21,7 +21,7 @@ import ch.cyberduck.core.ProgressListener; import ch.cyberduck.core.Session; import ch.cyberduck.core.cryptomator.CryptoTransferStatus; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.CryptoVaultInterface; 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 CryptoVaultInterface vault; - public CryptoUploadFeature(final Session session, final Upload delegate, final Write writer, final CryptoVault vault) { + public CryptoUploadFeature(final Session session, final Upload delegate, final Write writer, final CryptoVaultInterface vault) { this.session = session; this.proxy = delegate.withWriter(new CryptoWriteFeature<>(session, writer, vault)); 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 097729d6b7b..a243af0ef80 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 @@ -21,7 +21,7 @@ import ch.cyberduck.core.Session; import ch.cyberduck.core.cryptomator.CryptoOutputStream; import ch.cyberduck.core.cryptomator.CryptoTransferStatus; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.CryptoVaultInterface; 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 CryptoVaultInterface vault; - public CryptoWriteFeature(final Session session, final Write proxy, final CryptoVault vault) { + public CryptoWriteFeature(final Session session, final Write proxy, final CryptoVaultInterface vault) { this.session = session; this.proxy = proxy; this.vault = vault; From 909981fad03ee3623c20b1cc2eee897bf6d1aafd Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Mon, 20 Jan 2025 11:14:38 +0100 Subject: [PATCH 06/15] More extraction. --- .../core/cryptomator/AbstractVault.java | 218 ++++++++++++++++++ .../core/cryptomator/CryptoAclPermission.java | 5 +- .../cryptomator/CryptoTransferStatus.java | 4 +- .../core/cryptomator/CryptoVault.java | 183 +++------------ .../cryptomator/CryptoVaultInterface.java | 59 ----- .../cyberduck/core/cryptomator/UVFVault.java | 93 +++++++- .../features/CryptoBulkFeature.java | 6 +- .../features/CryptoChecksumCompute.java | 6 +- .../features/CryptoCopyFeature.java | 6 +- .../features/CryptoDeleteV6Feature.java | 6 +- .../features/CryptoDeleteV7Feature.java | 6 +- .../features/CryptoDirectoryV6Feature.java | 6 +- .../features/CryptoDirectoryV7Feature.java | 6 +- .../features/CryptoDownloadFeature.java | 6 +- .../features/CryptoMoveV6Feature.java | 6 +- .../features/CryptoMoveV7Feature.java | 6 +- .../features/CryptoMultipartWriteFeature.java | 6 +- .../features/CryptoReadFeature.java | 6 +- .../features/CryptoTimestampFeature.java | 6 +- .../features/CryptoTouchFeature.java | 6 +- .../features/CryptoUploadFeature.java | 6 +- .../features/CryptoWriteFeature.java | 6 +- 22 files changed, 379 insertions(+), 279 deletions(-) create mode 100644 cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java delete mode 100644 cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java 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..2b3ad755e36 --- /dev/null +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java @@ -0,0 +1,218 @@ +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.Session; +import ch.cyberduck.core.UrlProvider; +import ch.cyberduck.core.cryptomator.features.*; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.features.*; +import ch.cyberduck.core.preferences.PreferencesFactory; +import ch.cyberduck.core.shared.DefaultTouchFeature; +import ch.cyberduck.core.transfer.TransferStatus; + +import org.cryptomator.cryptolib.api.Cryptor; +import org.cryptomator.cryptolib.api.FileContentCryptor; +import org.cryptomator.cryptolib.api.FileHeaderCryptor; + +public abstract class AbstractVault implements Vault { + + public static final int VAULT_VERSION_DEPRECATED = 6; + public static final int VAULT_VERSION = PreferencesFactory.get().getInteger("cryptomator.vault.version"); + + public abstract Path getMasterkey(); + + public abstract Path getConfig(); + + public abstract int getVersion(); + + public abstract FileHeaderCryptor getFileHeaderCryptor(); + + public abstract FileContentCryptor getFileContentCryptor(); + + public abstract CryptorCache getFileNameCryptor(); + + public abstract CryptoFilename getFilenameProvider(); + + public abstract CryptoDirectory getDirectoryProvider(); + + public abstract Cryptor getCryptor(); + + public abstract int getNonceSize(); + + 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 Path encrypt(Session session, Path file) throws BackgroundException { + return this.encrypt(session, file, file.attributes().getDirectoryId(), false); + } + + @Override + public Path encrypt(Session session, Path file, boolean metadata) throws BackgroundException { + return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); + } + + public abstract Path encrypt(Session session, Path file, String directoryId, boolean metadata) throws BackgroundException; + + public synchronized boolean isUnlocked() { + return this.getCryptor() != null; + } + + @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._getFeature(Write.class)), session._getFeature(Write.class), this); + } + if(type == Directory.class) { + return (T) (this.getVersion() == VAULT_VERSION_DEPRECATED ? + new CryptoDirectoryV6Feature(session, (Directory) delegate, session._getFeature(Write.class), this) : + new CryptoDirectoryV7Feature(session, (Directory) delegate, session._getFeature(Write.class), this) + ); + } + if(type == Upload.class) { + return (T) new CryptoUploadFeature(session, (Upload) delegate, session._getFeature(Write.class), this); + } + if(type == Download.class) { + return (T) new CryptoDownloadFeature(session, (Download) delegate, session._getFeature(Read.class), 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) (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) (this.getVersion() == VAULT_VERSION_DEPRECATED ? + new CryptoDeleteV6Feature(session, (Delete) delegate, this) : + new CryptoDeleteV7Feature(session, (Delete) delegate, this)); + } + if(type == Trash.class) { + 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, session._getFeature(Delete.class), 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/CryptoAclPermission.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoAclPermission.java index eb7c1799ef2..19fe06f5047 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/CryptoTransferStatus.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoTransferStatus.java index 34da1e17f5b..7bdd884b96e 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 CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoTransferStatus(final CryptoVaultInterface vault, final TransferStatus proxy) { + public CryptoTransferStatus(final AbstractVault vault, final TransferStatus proxy) { super(proxy); this.vault = vault; this.withLength(vault.toCiphertextSize(proxy.getOffset(), proxy.getLength())) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 155574a72bb..43024637a79 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -15,8 +15,20 @@ * GNU General Public License for more details. */ -import ch.cyberduck.core.*; -import ch.cyberduck.core.cryptomator.features.*; +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.PathAttributes; +import ch.cyberduck.core.Permission; +import ch.cyberduck.core.Session; +import ch.cyberduck.core.SimplePathPredicate; +import ch.cyberduck.core.UUIDRandomStringService; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV6Provider; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV6Provider; @@ -26,10 +38,10 @@ 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.features.Directory; +import ch.cyberduck.core.features.Encryption; 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; @@ -81,11 +93,9 @@ // - upon create, the vault version is determined from preferences -> set the delegate impl // - upon unlock, the vault version needs to be determined by reading masterkey.cryptomator or (!) vault.uvf file -> set the delegate impl // - open is called either from create or unlock, hence at this point we can delegate calls to the v6/v7/uvf imple? -public class CryptoVault implements CryptoVaultInterface { +public class CryptoVault extends AbstractVault { 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"; @@ -385,10 +395,6 @@ private PerpetualMasterkey getMasterKey(final MasterkeyFile mkFile, final CharSe 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; @@ -548,29 +554,6 @@ public long toCiphertextSize(final long cleartextFileOffset, final long cleartex 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)) { @@ -594,6 +577,11 @@ public Path getConfig() { return config; } + @Override + public int getVersion() { + return vaultVersion; + } + @Override public FileHeaderCryptor getFileHeaderCryptor() { return cryptor.fileHeaderCryptor(); @@ -620,132 +608,13 @@ public CryptoDirectory getDirectoryProvider() { } @Override - public int getNonceSize() { - return nonceSize; - } - - @Override - public int numberOfChunks(final long cleartextFileSize) { - return (int) (cleartextFileSize / cryptor.fileContentCryptor().cleartextChunkSize() + - ((cleartextFileSize % cryptor.fileContentCryptor().cleartextChunkSize() > 0) ? 1 : 0)); + public Cryptor getCryptor() { + return cryptor; } @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._getFeature(Write.class)), session._getFeature(Write.class), this); - } - if(type == Directory.class) { - return (T) (vaultVersion == VAULT_VERSION_DEPRECATED ? - new CryptoDirectoryV6Feature(session, (Directory) delegate, session._getFeature(Write.class), this) : - new CryptoDirectoryV7Feature(session, (Directory) delegate, session._getFeature(Write.class), this) - ); - } - if(type == Upload.class) { - return (T) new CryptoUploadFeature(session, (Upload) delegate, session._getFeature(Write.class), this); - } - if(type == Download.class) { - return (T) new CryptoDownloadFeature(session, (Download) delegate, session._getFeature(Read.class), 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, session._getFeature(Delete.class), 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; + public int getNonceSize() { + return nonceSize; } @Override diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java deleted file mode 100644 index 83116b1fae6..00000000000 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVaultInterface.java +++ /dev/null @@ -1,59 +0,0 @@ -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.Path; -import ch.cyberduck.core.Session; -import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.Vault; - -import org.cryptomator.cryptolib.api.FileContentCryptor; -import org.cryptomator.cryptolib.api.FileHeaderCryptor; - -public interface CryptoVaultInterface extends Vault { - - Path getMasterkey(); - - Path getConfig(); - - FileHeaderCryptor getFileHeaderCryptor(); - - FileContentCryptor getFileContentCryptor(); - - CryptorCache getFileNameCryptor(); - - CryptoFilename getFilenameProvider(); - - CryptoDirectory getDirectoryProvider(); - - int getNonceSize(); - - int numberOfChunks(long cleartextFileSize); - - long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws CryptoInvalidFilesizeException; - - @Override - default Path encrypt(Session session, Path file) throws BackgroundException { - return this.encrypt(session, file, file.attributes().getDirectoryId(), false); - } - - @Override - default Path encrypt(Session session, Path file, boolean metadata) throws BackgroundException { - return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); - } - - Path encrypt(Session session, Path file, String directoryId, boolean metadata) throws BackgroundException; -} diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java index 6f7803728d6..06441ceaffe 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -19,22 +19,25 @@ 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.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.cryptomator.impl.CryptoFilenameV7Provider; import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.features.Vault; import ch.cyberduck.core.vault.VaultCredentials; 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.FileContentCryptor; +import org.cryptomator.cryptolib.api.FileHeaderCryptor; import org.cryptomator.cryptolib.api.UVFMasterkey; import java.util.EnumSet; +import java.util.Objects; -public class UVFVault implements Vault { +public class UVFVault extends AbstractVault { private static final Logger log = LogManager.getLogger(UVFVault.class); @@ -50,6 +53,8 @@ public class UVFVault implements Vault { private CryptoFilename filenameProvider; private CryptoDirectory directoryProvider; + private int nonceSize; + public UVFVault(final Path home, final String decryptedPayload) { this.home = home; this.decrypted = decryptedPayload; @@ -80,7 +85,7 @@ public UVFVault load(final Session session, final PasswordCallback prompt) th this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor()); this.filenameProvider = new CryptoFilenameV7Provider(/* TODO threshold was previously defined in vault.config - default now? */); this.directoryProvider = new CryptoDirectoryV7Provider(vault, filenameProvider, fileNameCryptor); - //TODO where? this.nonceSize = vaultConfig.getNonceSize(); + this.nonceSize = 12; return this; } @@ -95,28 +100,70 @@ public boolean contains(final Path file) { } @Override - public Path encrypt(final Session session, final Path file) throws BackgroundException { + public Path encrypt(final Session session, final Path file, final String directoryId, final boolean metadata) throws BackgroundException { return null; } @Override - public Path encrypt(final Session session, final Path file, final boolean metadata) throws BackgroundException { + public Path decrypt(final Session session, final Path file) throws BackgroundException { return null; } @Override - public Path decrypt(final Session session, final Path file) throws BackgroundException { + public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) { + return 0; + } + + @Override + public Path getMasterkey() { + //TODO: implement return null; } @Override - public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) { - return 0; + public Path getConfig() { + //TODO: implement + return null; } @Override - public long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws BackgroundException { - return 0; + public FileHeaderCryptor getFileHeaderCryptor() { + return cryptor.fileHeaderCryptor(); + } + + @Override + public FileContentCryptor getFileContentCryptor() { + return cryptor.fileContentCryptor(); + } + + @Override + public CryptorCache getFileNameCryptor() { + return fileNameCryptor; + } + + @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 int getVersion() { + return VAULT_VERSION; } @Override @@ -133,4 +180,30 @@ public State getState() { public Path getHome() { return null; } + + @Override + public boolean equals(final Object o) { + if(this == o) { + return true; + } + if(!(o instanceof UVFVault)) { + return false; + } + final UVFVault that = (UVFVault) 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/features/CryptoBulkFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java index 8c866c954d6..1d8947823e4 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.CryptoVaultInterface; +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; @@ -46,9 +46,9 @@ public class CryptoBulkFeature implements Bulk { private final Session session; private final Bulk delegate; - private final CryptoVaultInterface cryptomator; + private final AbstractVault cryptomator; - public CryptoBulkFeature(final Session session, final Bulk delegate, final Delete delete, final CryptoVaultInterface cryptomator) { + public CryptoBulkFeature(final Session session, final Bulk delegate, final Delete delete, final AbstractVault cryptomator) { this.session = session; this.delegate = delegate.withDelete(cryptomator.getFeature(session, Delete.class, delete)); this.cryptomator = cryptomator; 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 00c98ee147b..683b614fbda 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.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -55,10 +55,10 @@ public class CryptoChecksumCompute extends AbstractChecksumCompute { private static final Logger log = LogManager.getLogger(CryptoChecksumCompute.class); - private final CryptoVaultInterface cryptomator; + private final AbstractVault cryptomator; private final ChecksumCompute delegate; - public CryptoChecksumCompute(final ChecksumCompute delegate, final CryptoVaultInterface CryptoVaultInterface) { + 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 a4d37372387..855e152f33b 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.CryptoVaultInterface; +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 CryptoVaultInterface vault; + private final AbstractVault vault; private Session target; - public CryptoCopyFeature(final Session session, final Copy proxy, final CryptoVaultInterface 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 index 38862d62f57..dd5516d05de 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java @@ -20,8 +20,8 @@ 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.CryptoVaultInterface; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; @@ -44,10 +44,10 @@ public class CryptoDeleteV6Feature implements Delete, Trash { private final Session session; private final Delete proxy; - private final CryptoVaultInterface vault; + private final AbstractVault vault; private final CryptoFilename filenameProvider; - public CryptoDeleteV6Feature(final Session session, final Delete proxy, final CryptoVaultInterface vault) { + public CryptoDeleteV6Feature(final Session session, final Delete 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/CryptoDeleteV7Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV7Feature.java index da7ad1b78e1..b0a1f18a75a 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 @@ -21,8 +21,8 @@ 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.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; @@ -46,10 +46,10 @@ public class CryptoDeleteV7Feature implements Delete, Trash { private final Session session; private final Delete proxy; - private final CryptoVaultInterface vault; + private final AbstractVault vault; private final CryptoFilename filenameProvider; - public CryptoDeleteV7Feature(final Session session, final Delete proxy, final CryptoVaultInterface vault) { + public CryptoDeleteV7Feature(final Session session, final Delete 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/CryptoDirectoryV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java index bfebd38ca7c..2065d2e22f3 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java @@ -19,8 +19,8 @@ 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.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Directory; @@ -40,11 +40,11 @@ public class CryptoDirectoryV6Feature implements Directory { private final Session session; private final Write writer; private final Directory delegate; - private final CryptoVaultInterface vault; + private final AbstractVault vault; private final RandomStringService random = new UUIDRandomStringService(); public CryptoDirectoryV6Feature(final Session session, final Directory delegate, - final Write writer, final CryptoVaultInterface cryptomator) { + final Write writer, final AbstractVault cryptomator) { this.session = session; this.writer = writer; this.delegate = delegate; 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 aad32a4612f..45e85fa7be1 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 @@ -19,8 +19,8 @@ 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.CryptoVaultInterface; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -42,11 +42,11 @@ public class CryptoDirectoryV7Feature implements Directory { private final Session session; private final Write writer; private final Directory delegate; - private final CryptoVaultInterface vault; + private final AbstractVault vault; private final RandomStringService random = new UUIDRandomStringService(); public CryptoDirectoryV7Feature(final Session session, final Directory delegate, - final Write writer, final CryptoVaultInterface cryptomator) { + final Write writer, final AbstractVault cryptomator) { this.session = session; this.writer = writer; this.delegate = delegate; 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 20386818dba..a9ce5863e45 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,7 +19,7 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; -import ch.cyberduck.core.cryptomator.CryptoVaultInterface; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Download; @@ -32,9 +32,9 @@ public class CryptoDownloadFeature implements Download { private final Session session; private final Download proxy; - private final CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoDownloadFeature(final Session session, final Download proxy, final Read reader, final CryptoVaultInterface vault) { + public CryptoDownloadFeature(final Session session, final Download proxy, final Read reader, final AbstractVault vault) { this.session = session; this.proxy = proxy.withReader(new CryptoReadFeature(session, reader, vault)); 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 index bbbb60a0d86..9d7b89b8a6a 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMoveV6Feature.java @@ -19,7 +19,7 @@ import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; -import ch.cyberduck.core.cryptomator.CryptoVaultInterface; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; import ch.cyberduck.core.features.Delete; @@ -34,9 +34,9 @@ public class CryptoMoveV6Feature implements Move { private final Session session; private final Move proxy; - private final CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoMoveV6Feature(final Session session, final Move delegate, final CryptoVaultInterface cryptomator) { + public CryptoMoveV6Feature(final Session session, final Move delegate, final AbstractVault cryptomator) { this.session = session; this.proxy = delegate; this.vault = cryptomator; 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 6c3ab33c352..7fe877efc6a 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,7 +19,7 @@ import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; -import ch.cyberduck.core.cryptomator.CryptoVaultInterface; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; @@ -35,9 +35,9 @@ public class CryptoMoveV7Feature implements Move { private final Session session; private final Move proxy; - private final CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoMoveV7Feature(final Session session, final Move delegate, final CryptoVaultInterface cryptomator) { + public CryptoMoveV7Feature(final Session session, final Move delegate, final AbstractVault cryptomator) { this.session = session; this.proxy = delegate; this.vault = cryptomator; 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 f3f9f14ea2c..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.CryptoVaultInterface; +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 CryptoVaultInterface 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 CryptoVaultInterface 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 792201d7adf..779259f65d4 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.CryptoVaultInterface; 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 CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoReadFeature(final Session session, final Read proxy, final CryptoVaultInterface 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 70506559f95..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.CryptoVaultInterface; 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 CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoTimestampFeature(final Session session, final Timestamp proxy, final CryptoVaultInterface 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 793a8c1d7cf..09298d73a72 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.CryptoVaultInterface; +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 CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoTouchFeature(final Session session, final Touch proxy, final Write writer, final CryptoVaultInterface cryptomator) { + public CryptoTouchFeature(final Session session, final Touch proxy, final Write writer, final AbstractVault cryptomator) { this.session = session; this.proxy = proxy.withWriter(new CryptoWriteFeature<>(session, writer, cryptomator)); 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 7beecee0570..98047737d2c 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.CryptoVaultInterface; 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 CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoUploadFeature(final Session session, final Upload delegate, final Write writer, final CryptoVaultInterface vault) { + public CryptoUploadFeature(final Session session, final Upload delegate, final Write writer, final AbstractVault vault) { this.session = session; this.proxy = delegate.withWriter(new CryptoWriteFeature<>(session, writer, vault)); 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 a243af0ef80..49145e57d8b 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.CryptoVaultInterface; 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 CryptoVaultInterface vault; + private final AbstractVault vault; - public CryptoWriteFeature(final Session session, final Write proxy, final CryptoVaultInterface vault) { + public CryptoWriteFeature(final Session session, final Write proxy, final AbstractVault vault) { this.session = session; this.proxy = proxy; this.vault = vault; From db6ec89a8c86a0c48bea7e1845bfbdd893e61d43 Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Mon, 20 Jan 2025 15:22:37 +0100 Subject: [PATCH 07/15] More extraction. --- .../core/cryptomator/AbstractVault.java | 204 +++++++++++++++++- .../core/cryptomator/CryptoVault.java | 196 +---------------- .../cyberduck/core/cryptomator/UVFVault.java | 45 +--- 3 files changed, 220 insertions(+), 225 deletions(-) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java index 2b3ad755e36..7ee00625a0c 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java @@ -17,7 +17,10 @@ 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; @@ -26,19 +29,38 @@ 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.FileContentCryptor; import org.cryptomator.cryptolib.api.FileHeaderCryptor; +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.io.BaseEncoding; + public abstract class AbstractVault implements Vault { + private static final Logger log = LogManager.getLogger(AbstractVault.class); + public static final int VAULT_VERSION_DEPRECATED = 6; public static final int VAULT_VERSION = PreferencesFactory.get().getInteger("cryptomator.vault.version"); + 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"); + public abstract Path getMasterkey(); public abstract Path getConfig(); + public abstract Path gethHome(); + public abstract int getVersion(); public abstract FileHeaderCryptor getFileHeaderCryptor(); @@ -82,6 +104,26 @@ public long toCleartextSize(final long cleartextFileOffset, final long ciphertex } } + @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, file.attributes().getDirectoryId(), false); @@ -92,12 +134,172 @@ public Path encrypt(Session session, Path file, boolean metadata) throws Back return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); } - public abstract Path encrypt(Session session, Path file, String directoryId, boolean metadata) throws BackgroundException; + public Path encrypt(Session session, Path file, 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(this.gethHome())) { + 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().attributes().getDirectoryId(), decrypted.getParent()); + filename = this.getDirectoryProvider().toEncrypted(session, parent.attributes().getDirectoryId(), decrypted.getName(), decrypted.getType()); + } + else { + parent = this.getDirectoryProvider().toEncrypted(session, file.getParent().attributes().getDirectoryId(), file.getParent()); + filename = this.getDirectoryProvider().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 && this.getVersion() == 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 this.getDirectoryProvider().toEncrypted(session, this.gethHome().attributes().getDirectoryId(), this.gethHome()); + } + encrypted = this.getDirectoryProvider().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(this.gethHome()); + encrypted.attributes().setVault(this.gethHome()); + 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 = this.getVersion() == 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 = this.getFileNameCryptor().decryptFilename( + this.getVersion() == 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(this.gethHome()); + 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(this.getVersion() == VAULT_VERSION_DEPRECATED) { + return p.getName().startsWith(DIR_PREFIX); + } + 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.gethHome()) || file.isChild(this.getHome()); + } + return false; + } + + @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) { diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 43024637a79..4efa77bdd32 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -25,7 +25,6 @@ import ch.cyberduck.core.PasswordStoreFactory; 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.UUIDRandomStringService; @@ -50,7 +49,6 @@ 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; @@ -71,8 +69,6 @@ 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; @@ -81,7 +77,6 @@ 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; @@ -98,11 +93,6 @@ public class CryptoVault extends AbstractVault { 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"; @@ -326,18 +316,7 @@ public void unlock(final VaultConfig vaultConfig, final String passphrase, final @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(); - } - } + super.close(); cryptor = null; fileNameCryptor = null; } @@ -395,174 +374,6 @@ private PerpetualMasterkey getMasterKey(final MasterkeyFile mkFile, final CharSe new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase); } - @Override - public State getState() { - return this.isUnlocked() ? State.open : State.closed; - } - - @Override - public boolean contains(final Path file) { - if(this.isUnlocked()) { - return new SimplePathPredicate(file).test(home) || file.isChild(home); - } - return false; - } - - // UVF: extract to delegate? - 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); - } - - 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; } @@ -577,6 +388,11 @@ public Path getConfig() { return config; } + @Override + public Path gethHome() { + return home; + } + @Override public int getVersion() { return vaultVersion; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java index 06441ceaffe..44de24b237e 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -71,7 +71,7 @@ public UVFVault(final Path home, final String decryptedPayload) { @Override public Path create(final Session session, final String region, final VaultCredentials credentials) throws BackgroundException { - return null; + throw new UnsupportedOperationException(); } // load -> unlock -> open @@ -90,28 +90,10 @@ public UVFVault load(final Session session, final PasswordCallback prompt) th } @Override - public void close() { - - } - - @Override - public boolean contains(final Path file) { - return false; - } - - @Override - public Path encrypt(final Session session, final Path file, final String directoryId, final boolean metadata) throws BackgroundException { - return null; - } - - @Override - public Path decrypt(final Session session, final Path file) throws BackgroundException { - return null; - } - - @Override - public long toCiphertextSize(final long cleartextFileOffset, final long cleartextFileSize) { - return 0; + public synchronized void close() { + super.close(); + cryptor = null; + fileNameCryptor = null; } @Override @@ -126,6 +108,11 @@ public Path getConfig() { return null; } + @Override + public Path gethHome() { + return home; + } + @Override public FileHeaderCryptor getFileHeaderCryptor() { return cryptor.fileHeaderCryptor(); @@ -166,19 +153,9 @@ public int getVersion() { return VAULT_VERSION; } - @Override - public T getFeature(final Session session, final Class type, final T delegate) { - return null; - } - - @Override - public State getState() { - return null; - } - @Override public Path getHome() { - return null; + return home; } @Override From 80d67c4e6adabb76bcc9a1e63c058d3e1318972e Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Thu, 6 Feb 2025 14:02:22 +0100 Subject: [PATCH 08/15] Review. --- .../src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java index 44de24b237e..e706ebf9e90 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -55,7 +55,7 @@ public class UVFVault extends AbstractVault { private int nonceSize; - public UVFVault(final Path home, final String decryptedPayload) { + public UVFVault(final Path home, final String decryptedPayload, final String config, final byte[] pepper) { this.home = home; this.decrypted = decryptedPayload; // New vault home with vault flag set for internal use @@ -83,7 +83,7 @@ public UVFVault load(final Session session, final PasswordCallback prompt) th log.debug("Initialized crypto provider {}", provider); this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide()); this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor()); - this.filenameProvider = new CryptoFilenameV7Provider(/* TODO threshold was previously defined in vault.config - default now? */); + this.filenameProvider = new CryptoFilenameV7Provider(Integer.MAX_VALUE); // TODO there is no shortening in UVF defined yet this.directoryProvider = new CryptoDirectoryV7Provider(vault, filenameProvider, fileNameCryptor); this.nonceSize = 12; return this; From f68923509a4f523edec041d3e015cb206a5930f7 Mon Sep 17 00:00:00 2001 From: chenkins Date: Thu, 6 Feb 2025 17:17:52 +0100 Subject: [PATCH 09/15] Fix UnsupportedOperationException for fileNameCryptor() without revision. --- .../src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java index e706ebf9e90..8f7cdc93624 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -82,7 +82,7 @@ public UVFVault load(final Session session, final PasswordCallback prompt) th final CryptorProvider provider = CryptorProvider.forScheme(CryptorProvider.Scheme.UVF_DRAFT); log.debug("Initialized crypto provider {}", provider); this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide()); - this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor()); + this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor(masterKey.firstRevision())); // TODO revision eventually depends on location - safe? this.filenameProvider = new CryptoFilenameV7Provider(Integer.MAX_VALUE); // TODO there is no shortening in UVF defined yet this.directoryProvider = new CryptoDirectoryV7Provider(vault, filenameProvider, fileNameCryptor); this.nonceSize = 12; From 9b4eb00d1d7f4f4bea9ecc57861d0b7fc310b749 Mon Sep 17 00:00:00 2001 From: chenkins Date: Sat, 8 Feb 2025 13:48:19 +0100 Subject: [PATCH 10/15] Bump version to 9.1.3.uvfdraft-SNAPSHOT. --- azure/pom.xml | 2 +- backblaze/pom.xml | 2 +- binding/pom.xml | 2 +- bonjour/dll/pom.xml | 2 +- bonjour/native/pom.xml | 2 +- bonjour/pom.xml | 2 +- box/pom.xml | 2 +- brick/pom.xml | 2 +- cli/dll/pom.xml | 2 +- cli/linux/pom.xml | 2 +- cli/osx/pom.xml | 2 +- cli/pom.xml | 2 +- cli/windows/pom.xml | 2 +- core/dll/pom.xml | 2 +- core/dylib/pom.xml | 2 +- core/native/pom.xml | 2 +- core/native/refresh/pom.xml | 2 +- core/pom.xml | 2 +- cryptomator/dll/pom.xml | 2 +- cryptomator/pom.xml | 2 +- ctera/pom.xml | 2 +- deepbox/pom.xml | 2 +- defaults/pom.xml | 2 +- dracoon/pom.xml | 2 +- dropbox/pom.xml | 2 +- eue/pom.xml | 2 +- freenet/pom.xml | 2 +- ftp/pom.xml | 2 +- googledrive/pom.xml | 2 +- googlestorage/pom.xml | 2 +- hubic/pom.xml | 2 +- i18n/pom.xml | 2 +- importer/dll/pom.xml | 2 +- importer/pom.xml | 2 +- irods/pom.xml | 2 +- jersey/pom.xml | 2 +- manta/pom.xml | 2 +- nextcloud/pom.xml | 2 +- nio/pom.xml | 2 +- oauth/pom.xml | 2 +- onedrive/pom.xml | 2 +- openstack/pom.xml | 2 +- osx/pom.xml | 2 +- owncloud/pom.xml | 2 +- pom.xml | 2 +- profiles/pom.xml | 2 +- protocols/dll/pom.xml | 2 +- protocols/pom.xml | 2 +- s3/pom.xml | 2 +- smb/pom.xml | 2 +- spectra/pom.xml | 2 +- ssh/pom.xml | 2 +- storegate/pom.xml | 2 +- test/pom.xml | 2 +- tus/pom.xml | 2 +- webdav/pom.xml | 2 +- windows/pom.xml | 2 +- 57 files changed, 57 insertions(+), 57 deletions(-) diff --git a/azure/pom.xml b/azure/pom.xml index 05bf0335c00..abcc4487d47 100644 --- a/azure/pom.xml +++ b/azure/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT azure diff --git a/backblaze/pom.xml b/backblaze/pom.xml index 60913e04f18..f394300555b 100644 --- a/backblaze/pom.xml +++ b/backblaze/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT backblaze jar diff --git a/binding/pom.xml b/binding/pom.xml index 2eebc5ad269..5220b9a306e 100644 --- a/binding/pom.xml +++ b/binding/pom.xml @@ -19,7 +19,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT binding jar diff --git a/bonjour/dll/pom.xml b/bonjour/dll/pom.xml index 125939ec011..900bb962ae3 100644 --- a/bonjour/dll/pom.xml +++ b/bonjour/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Bonjour pom diff --git a/bonjour/native/pom.xml b/bonjour/native/pom.xml index 14d952b3ce9..3e552fb5f6b 100644 --- a/bonjour/native/pom.xml +++ b/bonjour/native/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Bonjour.Native pom diff --git a/bonjour/pom.xml b/bonjour/pom.xml index 948a4ad272a..3aaeb862396 100644 --- a/bonjour/pom.xml +++ b/bonjour/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 diff --git a/box/pom.xml b/box/pom.xml index f4a2e48acd9..c9f8834eb12 100644 --- a/box/pom.xml +++ b/box/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT box diff --git a/brick/pom.xml b/brick/pom.xml index 7450f3f5b9f..b53c5c2e1ae 100644 --- a/brick/pom.xml +++ b/brick/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT brick jar diff --git a/cli/dll/pom.xml b/cli/dll/pom.xml index aae3208a140..34f9ddd9202 100644 --- a/cli/dll/pom.xml +++ b/cli/dll/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Cli pom diff --git a/cli/linux/pom.xml b/cli/linux/pom.xml index 494b39c0a57..935cd9ab572 100644 --- a/cli/linux/pom.xml +++ b/cli/linux/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT cli-linux Cyberduck CLI Linux diff --git a/cli/osx/pom.xml b/cli/osx/pom.xml index fe4aa09eea9..37983296958 100644 --- a/cli/osx/pom.xml +++ b/cli/osx/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT cli-osx Cyberduck CLI Mac diff --git a/cli/pom.xml b/cli/pom.xml index 002c8163e0c..ef0a1e4bac9 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -19,7 +19,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT cli diff --git a/cli/windows/pom.xml b/cli/windows/pom.xml index 59550580c54..c964e88d18e 100644 --- a/cli/windows/pom.xml +++ b/cli/windows/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT cli-windows Cyberduck CLI Windows diff --git a/core/dll/pom.xml b/core/dll/pom.xml index f0d2e563603..b123d18f24a 100644 --- a/core/dll/pom.xml +++ b/core/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Core pom diff --git a/core/dylib/pom.xml b/core/dylib/pom.xml index 07eb309acb4..bcbcac5c802 100644 --- a/core/dylib/pom.xml +++ b/core/dylib/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT libcore diff --git a/core/native/pom.xml b/core/native/pom.xml index 35449ff8a7b..5ef67bbbc5c 100644 --- a/core/native/pom.xml +++ b/core/native/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Core.Native pom diff --git a/core/native/refresh/pom.xml b/core/native/refresh/pom.xml index 1a9401dabee..ee129e7bd97 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.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Core.Refresh pom diff --git a/core/pom.xml b/core/pom.xml index 6f891e500e1..e38097a5de7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT core jar diff --git a/cryptomator/dll/pom.xml b/cryptomator/dll/pom.xml index e789d1b5e3f..37e27551d2d 100644 --- a/cryptomator/dll/pom.xml +++ b/cryptomator/dll/pom.xml @@ -20,7 +20,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Cryptomator pom diff --git a/cryptomator/pom.xml b/cryptomator/pom.xml index 0b5a3fe4a80..ff13895b095 100644 --- a/cryptomator/pom.xml +++ b/cryptomator/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT cryptomator jar diff --git a/ctera/pom.xml b/ctera/pom.xml index e5a6ab2f541..b748ef0d652 100644 --- a/ctera/pom.xml +++ b/ctera/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT ctera jar diff --git a/deepbox/pom.xml b/deepbox/pom.xml index e369ff2157a..a023c70bc83 100644 --- a/deepbox/pom.xml +++ b/deepbox/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT deepbox diff --git a/defaults/pom.xml b/defaults/pom.xml index 1d720376643..bba2d526004 100644 --- a/defaults/pom.xml +++ b/defaults/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT defaults diff --git a/dracoon/pom.xml b/dracoon/pom.xml index 47b98d9f519..2c6ddd6a7fc 100644 --- a/dracoon/pom.xml +++ b/dracoon/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT dracoon diff --git a/dropbox/pom.xml b/dropbox/pom.xml index 204619ec257..dba5a6e487d 100644 --- a/dropbox/pom.xml +++ b/dropbox/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 dropbox diff --git a/eue/pom.xml b/eue/pom.xml index 0d2b0de5af5..7af86755c97 100644 --- a/eue/pom.xml +++ b/eue/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT eue jar diff --git a/freenet/pom.xml b/freenet/pom.xml index 47dede28b16..1cc67515406 100644 --- a/freenet/pom.xml +++ b/freenet/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT freenet jar diff --git a/ftp/pom.xml b/ftp/pom.xml index 61648eba10c..301af31d099 100644 --- a/ftp/pom.xml +++ b/ftp/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT ftp jar diff --git a/googledrive/pom.xml b/googledrive/pom.xml index 30b53cb5326..3bc84bcba46 100644 --- a/googledrive/pom.xml +++ b/googledrive/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 googledrive diff --git a/googlestorage/pom.xml b/googlestorage/pom.xml index 73f4aa5cfdd..0bab1508262 100644 --- a/googlestorage/pom.xml +++ b/googlestorage/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT googlestorage diff --git a/hubic/pom.xml b/hubic/pom.xml index b86233f24c2..31886ccc631 100644 --- a/hubic/pom.xml +++ b/hubic/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT hubic jar diff --git a/i18n/pom.xml b/i18n/pom.xml index 5a9746338ae..89a43779f38 100644 --- a/i18n/pom.xml +++ b/i18n/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 jar diff --git a/importer/dll/pom.xml b/importer/dll/pom.xml index dcfe952d3db..cc71cd7c948 100644 --- a/importer/dll/pom.xml +++ b/importer/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Importer pom diff --git a/importer/pom.xml b/importer/pom.xml index ce4f4ecf68c..c7ed91f9a8b 100644 --- a/importer/pom.xml +++ b/importer/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 diff --git a/irods/pom.xml b/irods/pom.xml index 0b465e748c4..dc9f5b27044 100644 --- a/irods/pom.xml +++ b/irods/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT irods jar diff --git a/jersey/pom.xml b/jersey/pom.xml index 06ecfec237a..e5decbbe847 100644 --- a/jersey/pom.xml +++ b/jersey/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT jersey diff --git a/manta/pom.xml b/manta/pom.xml index e642aef743d..47aa8e4aeb2 100644 --- a/manta/pom.xml +++ b/manta/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT manta diff --git a/nextcloud/pom.xml b/nextcloud/pom.xml index 67678153000..8a9eb0a1c8e 100644 --- a/nextcloud/pom.xml +++ b/nextcloud/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT nextcloud jar diff --git a/nio/pom.xml b/nio/pom.xml index c732d62f15f..ad3f251fbae 100644 --- a/nio/pom.xml +++ b/nio/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT nio jar diff --git a/oauth/pom.xml b/oauth/pom.xml index 7a1673edf22..d946b3de8c1 100644 --- a/oauth/pom.xml +++ b/oauth/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT oauth diff --git a/onedrive/pom.xml b/onedrive/pom.xml index 3379dc830bd..3334136eef3 100644 --- a/onedrive/pom.xml +++ b/onedrive/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT onedrive diff --git a/openstack/pom.xml b/openstack/pom.xml index 4647dfa94e4..8bc8394475a 100644 --- a/openstack/pom.xml +++ b/openstack/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT openstack jar diff --git a/osx/pom.xml b/osx/pom.xml index dd01834ba62..5a80921e45f 100644 --- a/osx/pom.xml +++ b/osx/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT osx jar diff --git a/owncloud/pom.xml b/owncloud/pom.xml index 056b5072ccf..8a79b524c00 100644 --- a/owncloud/pom.xml +++ b/owncloud/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT owncloud jar diff --git a/pom.xml b/pom.xml index 4a6a325f515..67d18635b25 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ parent Cyberduck pom - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT defaults diff --git a/profiles/pom.xml b/profiles/pom.xml index f76ad6b0e31..60d176cb1f4 100644 --- a/profiles/pom.xml +++ b/profiles/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT profiles jar diff --git a/protocols/dll/pom.xml b/protocols/dll/pom.xml index 1f3243aedbf..0437b3bbf4b 100644 --- a/protocols/dll/pom.xml +++ b/protocols/dll/pom.xml @@ -5,7 +5,7 @@ ch.cyberduck parent ../../pom.xml - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT Cyberduck.Protocols pom diff --git a/protocols/pom.xml b/protocols/pom.xml index a72a303d7ef..65112a833c1 100644 --- a/protocols/pom.xml +++ b/protocols/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 pom diff --git a/s3/pom.xml b/s3/pom.xml index f0ab68ba1fe..b4883422fae 100644 --- a/s3/pom.xml +++ b/s3/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT s3 jar diff --git a/smb/pom.xml b/smb/pom.xml index ecac86c920c..d4b27ed521c 100644 --- a/smb/pom.xml +++ b/smb/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT smb jar diff --git a/spectra/pom.xml b/spectra/pom.xml index b5ed51b2051..67c52aa609f 100644 --- a/spectra/pom.xml +++ b/spectra/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT spectra jar diff --git a/ssh/pom.xml b/ssh/pom.xml index 0bf36be8229..c90e614bda4 100644 --- a/ssh/pom.xml +++ b/ssh/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT ssh jar diff --git a/storegate/pom.xml b/storegate/pom.xml index a26f91e85f6..ef86af8f413 100644 --- a/storegate/pom.xml +++ b/storegate/pom.xml @@ -19,7 +19,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT storegate diff --git a/test/pom.xml b/test/pom.xml index e4a6d2dbffa..474f80dd688 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 pom diff --git a/tus/pom.xml b/tus/pom.xml index 9742e3382bb..2d66e75cebb 100644 --- a/tus/pom.xml +++ b/tus/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT tus jar diff --git a/webdav/pom.xml b/webdav/pom.xml index 8afa76658f5..33daec6778c 100644 --- a/webdav/pom.xml +++ b/webdav/pom.xml @@ -18,7 +18,7 @@ ch.cyberduck parent - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT webdav jar diff --git a/windows/pom.xml b/windows/pom.xml index c380891d918..4da4e1234a8 100644 --- a/windows/pom.xml +++ b/windows/pom.xml @@ -18,7 +18,7 @@ parent ch.cyberduck - 9.1.3-SNAPSHOT + 9.1.3.uvfdraft-SNAPSHOT 4.0.0 Cyberduck.Native From cbbe9b26c8e2cf013592277f46bdb613a875ac1b Mon Sep 17 00:00:00 2001 From: chenkins Date: Tue, 11 Feb 2025 10:56:36 +0100 Subject: [PATCH 11/15] Temporarily skip SFTPCryptomatorInteroperabilityTest because of dependency to cryptofs (incompatible with cryptolib uvfdraft). --- pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pom.xml b/pom.xml index 67d18635b25..9a65caf4d33 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ 8.0.312.b07-8 8u312b07 ch.cyberduck.test.IntegrationTest + ch.cyberduck.core.cryptomator.SFTPCryptomatorInteroperabilityTest @@ -554,6 +555,9 @@ ${project.build.directory} ${surefire.group.excluded} + + ${surefire.exclude} + false From aca202b4ad2eef23a99f3836cbaa205344432ff8 Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Mon, 24 Feb 2025 14:49:20 +0100 Subject: [PATCH 12/15] Switch to byte array for directory ids to avoid lossy data conversion. --- .../ch/cyberduck/core/PathAttributes.java | 6 ++-- .../core/cryptomator/AbstractVault.java | 5 ++- .../core/cryptomator/ContentReader.java | 15 +++++++++ .../core/cryptomator/CryptoDirectory.java | 4 +-- .../core/cryptomator/CryptorCache.java | 12 ++++--- .../features/CryptoBulkFeature.java | 3 +- .../features/CryptoDirectoryV6Feature.java | 6 ++-- .../features/CryptoDirectoryV7Feature.java | 6 ++-- .../impl/CryptoDirectoryV6Provider.java | 31 ++++++++++--------- .../impl/CryptoDirectoryV7Provider.java | 11 +++---- .../core/cryptomator/CryptorCacheTest.java | 11 ++++--- .../features/CryptoBulkFeatureTest.java | 2 +- 12 files changed, 65 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/ch/cyberduck/core/PathAttributes.java b/core/src/main/java/ch/cyberduck/core/PathAttributes.java index d94ed8880c6..62eba587522 100644 --- a/core/src/main/java/ch/cyberduck/core/PathAttributes.java +++ b/core/src/main/java/ch/cyberduck/core/PathAttributes.java @@ -158,7 +158,7 @@ public class PathAttributes extends Attributes implements Serializable { /** * Unique identifier for cryptomator */ - private String directoryId; + private byte[] directoryId; private Map custom = Collections.emptyMap(); @@ -526,11 +526,11 @@ public PathAttributes withLockId(final String lockId) { return this; } - public String getDirectoryId() { + public byte[] getDirectoryId() { return directoryId; } - public void setDirectoryId(final String directoryId) { + public void setDirectoryId(final byte[] directoryId) { this.directoryId = directoryId; } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java index 7ee00625a0c..7235412e5be 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java @@ -36,7 +36,6 @@ import org.cryptomator.cryptolib.api.FileContentCryptor; import org.cryptomator.cryptolib.api.FileHeaderCryptor; -import java.nio.charset.StandardCharsets; import java.util.EnumSet; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -134,7 +133,7 @@ public Path encrypt(Session session, Path file, boolean metadata) throws Back return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); } - public Path encrypt(Session session, Path file, String directoryId, boolean metadata) throws BackgroundException { + public Path encrypt(Session session, Path file, byte[] directoryId, boolean metadata) throws BackgroundException { final Path encrypted; if(file.isFile() || metadata) { if(file.getType().contains(Path.Type.vault)) { @@ -212,7 +211,7 @@ public Path decrypt(final Session session, final Path file) throws Background try { final String cleartextFilename = this.getFileNameCryptor().decryptFilename( this.getVersion() == VAULT_VERSION_DEPRECATED ? BaseEncoding.base32() : BaseEncoding.base64Url(), - ciphertext, file.getParent().attributes().getDirectoryId().getBytes(StandardCharsets.UTF_8)); + ciphertext, file.getParent().attributes().getDirectoryId()); final PathAttributes attributes = new PathAttributes(file.attributes()); if(this.isDirectory(inflated)) { if(Permission.EMPTY != attributes.getPermission()) { 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 1a768dd32d0..d07d450ff43 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().withLength(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().withLength(file.attributes().getSize()), new DisabledConnectionCallback()), StandardCharsets.UTF_8); 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..2fdfa62c617 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoDirectory.java @@ -32,7 +32,7 @@ public interface CryptoDirectory { * @param type File type * @return Encrypted filename */ - String toEncrypted(Session session, String directoryId, String filename, EnumSet type) throws BackgroundException; + String toEncrypted(Session session, byte[] directoryId, String filename, EnumSet type) throws BackgroundException; /** * Get encrypted reference for clear text directory path. @@ -41,7 +41,7 @@ public interface CryptoDirectory { * @param directoryId Directory ID or null to read directory id from metadata file * @param directory Clear text */ - Path toEncrypted(Session session, String directoryId, Path directory) throws BackgroundException; + Path toEncrypted(Session session, byte[] directoryId, Path directory) throws BackgroundException; /** * Remove from cache 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/CryptoBulkFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoBulkFeature.java index 1d8947823e4..aa6357876b2 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 @@ -34,6 +34,7 @@ import org.cryptomator.cryptolib.api.FileHeader; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -84,7 +85,7 @@ public int compare(final Map.Entry o1, final Map.E 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(); + final byte[] directoryId = random.random().getBytes(StandardCharsets.US_ASCII); encrypted.put(new TransferItem(cryptomator.encrypt(session, file, directoryId, false), local), status); break; default: diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java index 2065d2e22f3..7b272ae5d58 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDirectoryV6Feature.java @@ -53,12 +53,12 @@ public CryptoDirectoryV6Feature(final Session session, final Directory @Override public Path mkdir(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 Path encrypt = vault.encrypt(session, folder, random.random().getBytes(StandardCharsets.US_ASCII), false); + final byte[] directoryId = encrypt.attributes().getDirectoryId(); // Create metadata file for directory final Path directoryMetadataFile = vault.encrypt(session, folder, true); log.debug("Write metadata {} for folder {}", directoryMetadataFile, folder); - new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8)); + new ContentWriter(session).write(directoryMetadataFile, directoryId); final Path intermediate = encrypt.getParent(); if(!session._getFeature(Find.class).find(intermediate)) { session._getFeature(Directory.class).mkdir(intermediate, new TransferStatus().withRegion(status.getRegion())); 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 45e85fa7be1..1f24ac25ec2 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 @@ -55,8 +55,8 @@ public CryptoDirectoryV7Feature(final Session session, final Directory @Override public Path mkdir(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 Path encrypt = vault.encrypt(session, folder, random.random().getBytes(StandardCharsets.US_ASCII), false); + final byte[] directoryId = encrypt.attributes().getDirectoryId(); // Create metadata file for directory final Path directoryMetadataFolder = session._getFeature(Directory.class).mkdir(vault.encrypt(session, folder, true), new TransferStatus().withRegion(status.getRegion())); @@ -64,7 +64,7 @@ public Path mkdir(final Path folder, final TransferStatus status) throws Backgro CryptoDirectoryV7Provider.DIRECTORY_METADATAFILE, EnumSet.of(Path.Type.file)); log.debug("Write metadata {} for folder {}", directoryMetadataFile, folder); - new ContentWriter(session).write(directoryMetadataFile, directoryId.getBytes(StandardCharsets.UTF_8)); + new ContentWriter(session).write(directoryMetadataFile, directoryId); final Path intermediate = encrypt.getParent(); if(!session._getFeature(Find.class).find(intermediate)) { session._getFeature(Directory.class).mkdir(intermediate, new TransferStatus().withRegion(status.getRegion())); 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 index 9ad0728a9c4..13b991b8f49 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java @@ -32,11 +32,12 @@ import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.preferences.PreferencesFactory; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.EnumSet; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -45,7 +46,7 @@ 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 static final byte[] ROOT_DIR_ID = new byte[0]; private final Path dataRoot; private final Path home; @@ -57,7 +58,7 @@ public class CryptoDirectoryV6Provider implements CryptoDirectory { private final Lock lock = new ReentrantLock(); - private final LRUCache, String> cache = LRUCache.build( + private final LRUCache, byte[]> cache = LRUCache.build( PreferencesFactory.get().getInteger("cryptomator.cache.size")); public CryptoDirectoryV6Provider(final Path vault, final CryptoFilename filenameProvider, final CryptorCache filenameCryptor) { @@ -68,15 +69,15 @@ public CryptoDirectoryV6Provider(final Path vault, final CryptoFilename filename } @Override - public String toEncrypted(final Session session, final String directoryId, final String filename, final EnumSet type) throws BackgroundException { + public String toEncrypted(final Session session, final byte[] directoryId, final String filename, final EnumSet type) throws BackgroundException { final String prefix = type.contains(Path.Type.directory) ? CryptoVault.DIR_PREFIX : ""; - final String ciphertextName = prefix + filenameCryptor.encryptFilename(CryptorCache.BASE32, filename, directoryId.getBytes(StandardCharsets.UTF_8)); + final String ciphertextName = prefix + filenameCryptor.encryptFilename(CryptorCache.BASE32, filename, directoryId); log.debug("Encrypted filename {} to {}", filename, ciphertextName); return filenameProvider.deflate(session, ciphertextName); } @Override - public Path toEncrypted(final Session session, final String directoryId, final Path directory) throws BackgroundException { + public Path toEncrypted(final Session session, final byte[] directoryId, final Path directory) throws BackgroundException { if(!directory.isDirectory()) { throw new NotfoundException(directory.getAbsolute()); } @@ -86,7 +87,7 @@ public Path toEncrypted(final Session session, final String directoryId, fina attributes.withVersionId(null); attributes.withFileId(null); // Remember random directory id for use in vault - final String id = this.toDirectoryId(session, directory, directoryId); + final byte[] id = this.toDirectoryId(session, directory, directoryId); log.debug("Use directory ID '{}' for folder {}", id, directory); attributes.setDirectoryId(id); attributes.setDecrypted(directory); @@ -102,18 +103,18 @@ public Path toEncrypted(final Session session, final String directoryId, fina throw new NotfoundException(directory.getAbsolute()); } - private String toDirectoryId(final Session session, final Path directory, final String directoryId) throws BackgroundException { + private byte[] toDirectoryId(final Session session, final Path directory, final byte[] directoryId) throws BackgroundException { if(new SimplePathPredicate(home).test(directory)) { return ROOT_DIR_ID; } - if(StringUtils.isBlank(directoryId)) { + if(ArrayUtils.isEmpty(directoryId)) { if(cache.contains(new SimplePathPredicate(directory))) { return cache.get(new SimplePathPredicate(directory)); } try { log.debug("Acquire lock for {}", directory); lock.lock(); - final String id = this.load(session, directory); + final byte[] id = this.load(session, directory); cache.put(new SimplePathPredicate(directory), id); return id; } @@ -125,15 +126,15 @@ private String toDirectoryId(final Session session, final Path directory, fin cache.put(new SimplePathPredicate(directory), directoryId); } else { - final String existing = cache.get(new SimplePathPredicate(directory)); - if(!existing.equals(directoryId)) { + final byte[] existing = cache.get(new SimplePathPredicate(directory)); + if(!Arrays.equals(existing, directoryId)) { log.warn("Do not override already cached id {} with {}", existing, directoryId); } } return cache.get(new SimplePathPredicate(directory)); } - protected String load(final Session session, final Path directory) throws BackgroundException { + protected byte[] 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)); @@ -141,11 +142,11 @@ protected String load(final Session session, final Path directory) throws Bac 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); + return new ContentReader(session).readBytes(metadataFile); } catch(NotfoundException e) { log.warn("Missing directory ID for folder {}", directory); - return random.random(); + return random.random().getBytes(StandardCharsets.US_ASCII); } } 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 index cfb9a8b55e2..484a33b7657 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV7Provider.java @@ -55,14 +55,13 @@ public CryptoDirectoryV7Provider(final Path vault, final CryptoFilename filename } @Override - public String toEncrypted(final Session session, final String directoryId, final String filename, final EnumSet type) throws BackgroundException { - final String ciphertextName = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), - filename, directoryId.getBytes(StandardCharsets.UTF_8)) + EXTENSION_REGULAR; + public String toEncrypted(final Session session, final byte[] directoryId, final String filename, final EnumSet type) throws BackgroundException { + final String ciphertextName = filenameCryptor.encryptFilename(BaseEncoding.base64Url(), filename, directoryId) + EXTENSION_REGULAR; log.debug("Encrypted filename {} to {}", filename, ciphertextName); return filenameProvider.deflate(session, ciphertextName); } - protected String load(final Session session, final Path directory) throws BackgroundException { + protected byte[] 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)); @@ -71,11 +70,11 @@ protected String load(final Session session, final Path directory) throws Bac 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); + return new ContentReader(session).readBytes(metadataFile); } catch(NotfoundException e) { log.warn("Missing directory ID for folder {}", directory); - return random.random(); + return random.random().getBytes(StandardCharsets.US_ASCII); } } } 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 52f12e31f97..6499bae0cb2 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 @@ -105,7 +105,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 From 5136207a391c6ca82a7af54ebc0c036a2a492055 Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Tue, 25 Feb 2025 12:18:39 +0100 Subject: [PATCH 13/15] Directory id for root is derived from key material. --- .../cyberduck/core/cryptomator/UVFVault.java | 4 +- .../impl/CryptoDirectoryUVFProvider.java | 41 +++++++++++++++++++ .../impl/CryptoDirectoryV6Provider.java | 2 +- 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java index 8f7cdc93624..d6a038ffc85 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -20,7 +20,7 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Session; import ch.cyberduck.core.SimplePathPredicate; -import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; +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; @@ -84,7 +84,7 @@ public UVFVault load(final Session session, final PasswordCallback prompt) th this.cryptor = provider.provide(masterKey, FastSecureRandomProvider.get().provide()); this.fileNameCryptor = new CryptorCache(cryptor.fileNameCryptor(masterKey.firstRevision())); // TODO revision eventually depends on location - safe? this.filenameProvider = new CryptoFilenameV7Provider(Integer.MAX_VALUE); // TODO there is no shortening in UVF defined yet - this.directoryProvider = new CryptoDirectoryV7Provider(vault, filenameProvider, fileNameCryptor); + this.directoryProvider = new CryptoDirectoryUVFProvider(vault, filenameProvider, fileNameCryptor); this.nonceSize = 12; return this; } 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..db8900e5854 --- /dev/null +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryUVFProvider.java @@ -0,0 +1,41 @@ +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.CryptoFilename; +import ch.cyberduck.core.cryptomator.CryptorCache; +import ch.cyberduck.core.exception.BackgroundException; + +public class CryptoDirectoryUVFProvider extends CryptoDirectoryV7Provider { + + private final Path home; + + public CryptoDirectoryUVFProvider(final Path vault, final CryptoFilename filenameProvider, final CryptorCache filenameCryptor) { + super(vault, filenameProvider, filenameCryptor); + this.home = vault; + } + + @Override + protected byte[] toDirectoryId(final Session session, final Path directory, final byte[] directoryId) throws BackgroundException { + if(new SimplePathPredicate(home).test(directory)) { + return directoryId; + } + return super.toDirectoryId(session, directory, directoryId); + } +} 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 index 13b991b8f49..1d7e236452a 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/CryptoDirectoryV6Provider.java @@ -103,7 +103,7 @@ public Path toEncrypted(final Session session, final byte[] directoryId, fina throw new NotfoundException(directory.getAbsolute()); } - private byte[] toDirectoryId(final Session session, final Path directory, final byte[] directoryId) throws BackgroundException { + protected byte[] toDirectoryId(final Session session, final Path directory, final byte[] directoryId) throws BackgroundException { if(new SimplePathPredicate(home).test(directory)) { return ROOT_DIR_ID; } From 45d92324fc2e55e8a1dfced34b80e5e3ecb11259 Mon Sep 17 00:00:00 2001 From: chenkins Date: Tue, 25 Feb 2025 14:09:01 +0100 Subject: [PATCH 14/15] Fix cryptolib uvfdraft version. --- cryptomator/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cryptomator/pom.xml b/cryptomator/pom.xml index ff13895b095..d78c32f369f 100644 --- a/cryptomator/pom.xml +++ b/cryptomator/pom.xml @@ -25,7 +25,7 @@ jar - 2.3.0-uvfdraft-SNAPSHOT + 2.3.0.uvfdraft-SNAPSHOT From f26423bbf40da2007d3b6bcb73544ae29b0d8f4b Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Wed, 26 Feb 2025 15:53:16 +0100 Subject: [PATCH 15/15] Fix typo. --- .../cyberduck/core/cryptomator/AbstractVault.java | 14 ++++++-------- .../ch/cyberduck/core/cryptomator/CryptoVault.java | 5 ----- .../ch/cyberduck/core/cryptomator/UVFVault.java | 7 +------ 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java index 7235412e5be..d5728367c2e 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java @@ -58,8 +58,6 @@ public abstract class AbstractVault implements Vault { public abstract Path getConfig(); - public abstract Path gethHome(); - public abstract int getVersion(); public abstract FileHeaderCryptor getFileHeaderCryptor(); @@ -140,7 +138,7 @@ public Path encrypt(Session session, Path file, byte[] directoryId, boolean m log.warn("Skip file {} because it is marked as an internal vault path", file); return file; } - if(new SimplePathPredicate(file).test(this.gethHome())) { + if(new SimplePathPredicate(file).test(this.getHome())) { log.warn("Skip vault home {} because the root has no metadata file", file); return file; } @@ -179,7 +177,7 @@ public Path encrypt(Session session, Path file, byte[] directoryId, boolean m return file; } if(file.getType().contains(Path.Type.vault)) { - return this.getDirectoryProvider().toEncrypted(session, this.gethHome().attributes().getDirectoryId(), this.gethHome()); + return this.getDirectoryProvider().toEncrypted(session, this.getHome().attributes().getDirectoryId(), this.getHome()); } encrypted = this.getDirectoryProvider().toEncrypted(session, directoryId, file); } @@ -188,8 +186,8 @@ public Path encrypt(Session session, Path file, byte[] directoryId, boolean m encrypted.attributes().setDecrypted(file); } // Add reference for vault - file.attributes().setVault(this.gethHome()); - encrypted.attributes().setVault(this.gethHome()); + file.attributes().setVault(this.getHome()); + encrypted.attributes().setVault(this.getHome()); return encrypted; } @@ -233,7 +231,7 @@ public Path decrypt(final Session session, final Path file) throws Background // Add reference to encrypted file attributes.setEncrypted(file); // Add reference for vault - attributes.setVault(this.gethHome()); + attributes.setVault(this.getHome()); 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); @@ -279,7 +277,7 @@ public synchronized boolean isUnlocked() { @Override public boolean contains(final Path file) { if(this.isUnlocked()) { - return new SimplePathPredicate(file).test(this.gethHome()) || file.isChild(this.getHome()); + return new SimplePathPredicate(file).test(this.getHome()) || file.isChild(this.getHome()); } return false; } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java index 4efa77bdd32..ac3bafaf771 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/CryptoVault.java @@ -388,11 +388,6 @@ public Path getConfig() { return config; } - @Override - public Path gethHome() { - return home; - } - @Override public int getVersion() { return vaultVersion; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java index d6a038ffc85..8c650625ad7 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -109,7 +109,7 @@ public Path getConfig() { } @Override - public Path gethHome() { + public Path getHome() { return home; } @@ -153,11 +153,6 @@ public int getVersion() { return VAULT_VERSION; } - @Override - public Path getHome() { - return home; - } - @Override public boolean equals(final Object o) { if(this == o) {