diff --git a/cryptomator/pom.xml b/cryptomator/pom.xml index ea747d7fd5f..cdea7324f45 100644 --- a/cryptomator/pom.xml +++ b/cryptomator/pom.xml @@ -25,7 +25,7 @@ jar - 2.1.2.1 + 2.3.0-uvfdraft-SNAPSHOT diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java new file mode 100644 index 00000000000..7ee00625a0c --- /dev/null +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/AbstractVault.java @@ -0,0 +1,420 @@ +package ch.cyberduck.core.cryptomator; + +/* + * Copyright (c) 2002-2025 iterate GmbH. All rights reserved. + * https://cyberduck.io/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +import ch.cyberduck.core.ListService; +import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; +import ch.cyberduck.core.Permission; +import ch.cyberduck.core.Session; +import ch.cyberduck.core.SimplePathPredicate; +import ch.cyberduck.core.UrlProvider; +import ch.cyberduck.core.cryptomator.features.*; +import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.features.*; +import ch.cyberduck.core.preferences.PreferencesFactory; +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(); + + 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 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); + } + + @Override + public Path encrypt(Session session, Path file, boolean metadata) throws BackgroundException { + return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); + } + + 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) { + 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 fc5e06559f8..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 CryptoVault vault; + private final AbstractVault vault; - public CryptoTransferStatus(final CryptoVault vault, final TransferStatus proxy) { + public CryptoTransferStatus(final AbstractVault vault, final TransferStatus proxy) { super(proxy); this.vault = vault; this.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 42535c125eb..4efa77bdd32 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,19 @@ * 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.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 +37,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; @@ -38,13 +49,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.cryptomator.cryptolib.api.AuthenticationFailedException; import org.cryptomator.cryptolib.api.Cryptor; import org.cryptomator.cryptolib.api.CryptorProvider; import org.cryptomator.cryptolib.api.FileContentCryptor; import org.cryptomator.cryptolib.api.FileHeaderCryptor; import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.Masterkey; +import org.cryptomator.cryptolib.api.PerpetualMasterkey; import org.cryptomator.cryptolib.common.MasterkeyFile; import org.cryptomator.cryptolib.common.MasterkeyFileAccess; @@ -58,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; @@ -68,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; @@ -76,18 +84,15 @@ /** * Cryptomator vault implementation */ -public class CryptoVault implements Vault { +// 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 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"; - - 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"; @@ -114,6 +119,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 +127,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 +141,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()) { @@ -150,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 { @@ -219,6 +230,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 +247,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 +257,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 +270,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 +278,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; @@ -300,22 +316,12 @@ 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; } + // 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: @@ -325,24 +331,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, at this point we know which version protected void open(final VaultConfig vaultConfig, final CharSequence passphrase) throws BackgroundException { - this.open(vaultConfig, passphrase, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig)); - } - - protected void open(final VaultConfig vaultConfig, final CharSequence passphrase, - final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException { try { - final Masterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase); - this.open(vaultConfig, masterKey, filenameProvider, directoryProvider); + final PerpetualMasterkey masterKey = this.getMasterKey(vaultConfig.getMkfile(), passphrase); + this.open(vaultConfig, masterKey); } catch(IllegalArgumentException | IOException e) { throw new VaultException("Failure reading key file", e); @@ -352,391 +354,83 @@ protected void open(final VaultConfig vaultConfig, final CharSequence passphrase } } - protected void open(final VaultConfig vaultConfig, final Masterkey masterKey) throws BackgroundException { - this.open(vaultConfig, masterKey, this.createFilenameProvider(vaultConfig), this.createDirectoryProvider(vaultConfig)); - } - - protected void open(final VaultConfig vaultConfig, final Masterkey masterKey, - final CryptoFilename filenameProvider, final CryptoDirectory directoryProvider) throws BackgroundException { + // UVF: unused?! + protected void open(final VaultConfig vaultConfig, final PerpetualMasterkey masterKey) 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(); } - 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( new ByteArrayInputStream(writer.getBuffer().toString().getBytes(StandardCharsets.UTF_8)), passphrase); } - public synchronized boolean isUnlocked() { - return cryptor != null; - } - - @Override - public State getState() { - return this.isUnlocked() ? State.open : State.closed; - } - - @Override - public boolean contains(final Path file) { - if(this.isUnlocked()) { - return new SimplePathPredicate(file).test(home) || file.isChild(home); - } - return false; + public Path getHome() { + return home; } @Override - public Path encrypt(final Session session, final Path file) throws BackgroundException { - return this.encrypt(session, file, file.attributes().getDirectoryId(), false); + public Path getMasterkey() { + return masterkey; } @Override - public Path encrypt(final Session session, final Path file, boolean metadata) throws BackgroundException { - return this.encrypt(session, file, file.attributes().getDirectoryId(), metadata); - } - - public Path encrypt(final Session session, final Path file, final String directoryId, boolean metadata) throws BackgroundException { - final Path encrypted; - if(file.isFile() || metadata) { - if(file.getType().contains(Path.Type.vault)) { - log.warn("Skip file {} because it is marked as an internal vault path", file); - return file; - } - if(new SimplePathPredicate(file).test(home)) { - log.warn("Skip vault home {} because the root has no metadata file", file); - return file; - } - final Path parent; - final String filename; - if(file.getType().contains(Path.Type.encrypted)) { - final Path decrypted = file.attributes().getDecrypted(); - parent = directoryProvider.toEncrypted(session, decrypted.getParent().attributes().getDirectoryId(), decrypted.getParent()); - filename = directoryProvider.toEncrypted(session, parent.attributes().getDirectoryId(), decrypted.getName(), decrypted.getType()); - } - else { - parent = directoryProvider.toEncrypted(session, file.getParent().attributes().getDirectoryId(), file.getParent()); - filename = directoryProvider.toEncrypted(session, parent.attributes().getDirectoryId(), file.getName(), file.getType()); - } - final PathAttributes attributes = new PathAttributes(file.attributes()); - attributes.setDirectoryId(null); - if(!file.isFile() && !metadata) { - // The directory is different from the metadata file used to resolve the actual folder - attributes.setVersionId(null); - attributes.setFileId(null); - } - // Translate file size - attributes.setSize(this.toCiphertextSize(0L, file.attributes().getSize())); - final EnumSet type = EnumSet.copyOf(file.getType()); - if(metadata && vaultVersion == VAULT_VERSION_DEPRECATED) { - type.remove(Path.Type.directory); - type.add(Path.Type.file); - } - type.remove(Path.Type.decrypted); - type.add(Path.Type.encrypted); - encrypted = new Path(parent, filename, type, attributes); - } - else { - if(file.getType().contains(Path.Type.encrypted)) { - log.warn("Skip file {} because it is already marked as an encrypted path", file); - return file; - } - if(file.getType().contains(Path.Type.vault)) { - return directoryProvider.toEncrypted(session, home.attributes().getDirectoryId(), home); - } - encrypted = directoryProvider.toEncrypted(session, directoryId, file); - } - // Add reference to decrypted file - if(!file.getType().contains(Path.Type.encrypted)) { - encrypted.attributes().setDecrypted(file); - } - // Add reference for vault - file.attributes().setVault(home); - encrypted.attributes().setVault(home); - return encrypted; + public Path getConfig() { + return config; } @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(); + public Path gethHome() { + return home; } @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); + public int getVersion() { + return vaultVersion; } @Override - public long toCleartextSize(final long cleartextFileOffset, final long ciphertextFileSize) throws CryptoInvalidFilesizeException { - if(TransferStatus.UNKNOWN_LENGTH == ciphertextFileSize) { - return TransferStatus.UNKNOWN_LENGTH; - } - final int headerSize; - if(0L == cleartextFileOffset) { - headerSize = cryptor.fileHeaderCryptor().headerSize(); - } - else { - headerSize = 0; - } - try { - return cryptor.fileContentCryptor().cleartextSize(ciphertextFileSize - headerSize); - } - catch(AssertionError e) { - throw new CryptoInvalidFilesizeException(String.format("Encrypted file size must be at least %d bytes", headerSize)); - } - catch(IllegalArgumentException e) { - throw new CryptoInvalidFilesizeException(String.format("Invalid file size. %s", e.getMessage())); - } - } - - private Path inflate(final Session session, final Path file) throws BackgroundException { - final String fileName = file.getName(); - if(filenameProvider.isDeflated(fileName)) { - final String filename = filenameProvider.inflate(session, fileName); - return new Path(file.getParent(), filename, EnumSet.of(Path.Type.file), file.attributes()); - } - return file; - } - - public Path getHome() { - return home; - } - - public Path getMasterkey() { - return masterkey; - } - - public Path getConfig() { - return config; - } - public FileHeaderCryptor getFileHeaderCryptor() { return cryptor.fileHeaderCryptor(); } + @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; } - public int getNonceSize() { - return nonceSize; - } - - public int numberOfChunks(final long cleartextFileSize) { - return (int) (cleartextFileSize / cryptor.fileContentCryptor().cleartextChunkSize() + - ((cleartextFileSize % cryptor.fileContentCryptor().cleartextChunkSize() > 0) ? 1 : 0)); + @Override + 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/UVFVault.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java new file mode 100644 index 00000000000..44de24b237e --- /dev/null +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/UVFVault.java @@ -0,0 +1,186 @@ +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.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.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 extends AbstractVault { + + 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; + + private int nonceSize; + + 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 { + throw new UnsupportedOperationException(); + } + + // 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); + this.nonceSize = 12; + return this; + } + + @Override + public synchronized void close() { + super.close(); + cryptor = null; + fileNameCryptor = null; + } + + @Override + public Path getMasterkey() { + //TODO: implement + return null; + } + + @Override + public Path getConfig() { + //TODO: implement + return null; + } + + @Override + public Path gethHome() { + return home; + } + + @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 Cryptor getCryptor() { + return cryptor; + } + + @Override + public int getNonceSize() { + return nonceSize; + } + + @Override + public int getVersion() { + return VAULT_VERSION; + } + + @Override + public Path getHome() { + return home; + } + + @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 c16210fc644..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.CryptoVault; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -46,9 +46,9 @@ public class CryptoBulkFeature implements Bulk { private final Session session; private final Bulk delegate; - private final CryptoVault cryptomator; + private final AbstractVault cryptomator; - public CryptoBulkFeature(final Session session, final Bulk delegate, final Delete delete, final CryptoVault 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 51d4dc0c635..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.CryptoVault; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -55,11 +55,11 @@ public class CryptoChecksumCompute extends AbstractChecksumCompute { private static final Logger log = LogManager.getLogger(CryptoChecksumCompute.class); - private final CryptoVault cryptomator; + private final AbstractVault cryptomator; private final ChecksumCompute delegate; - public CryptoChecksumCompute(final ChecksumCompute delegate, final CryptoVault vault) { - this.cryptomator = vault; + public CryptoChecksumCompute(final ChecksumCompute delegate, final AbstractVault CryptoVaultInterface) { + this.cryptomator = CryptoVaultInterface; this.delegate = delegate; } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java index 5cbb40a94e9..cc0733ed38f 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoCopyFeature.java @@ -19,7 +19,7 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.Session; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -36,11 +36,11 @@ public class CryptoCopyFeature implements Copy { private final Session session; private final Copy proxy; - private final CryptoVault vault; + private final AbstractVault vault; private Session target; - public CryptoCopyFeature(final Session session, final Copy proxy, final CryptoVault vault) { + public CryptoCopyFeature(final Session session, final Copy proxy, final AbstractVault vault) { this.session = session; this.target = session; this.proxy = proxy; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoDeleteV6Feature.java index aa20ce52cff..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.CryptoVault; 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 AbstractVault 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 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 611988cb485..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.CryptoVault; 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 AbstractVault vault; private final CryptoFilename filenameProvider; - public CryptoDeleteV7Feature(final Session session, final Delete proxy, final CryptoVault vault) { + public CryptoDeleteV7Feature(final Session session, final Delete proxy, final AbstractVault vault) { this.session = session; this.proxy = proxy; this.vault = vault; 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..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.CryptoVault; 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 AbstractVault 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 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 17547e2793c..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.CryptoVault; 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 AbstractVault 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 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 dc803e4c911..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,12 +19,11 @@ import ch.cyberduck.core.Local; import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Download; import ch.cyberduck.core.features.Read; -import ch.cyberduck.core.features.Vault; import ch.cyberduck.core.io.BandwidthThrottle; import ch.cyberduck.core.io.StreamListener; import ch.cyberduck.core.transfer.TransferStatus; @@ -33,9 +32,9 @@ public class CryptoDownloadFeature implements Download { private final Session session; private final Download proxy; - private final Vault vault; + private final AbstractVault vault; - public CryptoDownloadFeature(final Session session, final Download proxy, final Read reader, final CryptoVault 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/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 20612ca011b..04b9cc14968 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.AbstractVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; import ch.cyberduck.core.features.Delete; @@ -33,9 +33,9 @@ public class CryptoMoveV6Feature implements Move { private final Session session; private final Move proxy; - private final CryptoVault vault; + private final AbstractVault vault; - public CryptoMoveV6Feature(final Session session, final Move delegate, final CryptoVault 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 433e6b9c152..c302318cfaa 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.AbstractVault; import ch.cyberduck.core.cryptomator.impl.CryptoDirectoryV7Provider; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; @@ -34,9 +34,9 @@ public class CryptoMoveV7Feature implements Move { private final Session session; private final Move proxy; - private final CryptoVault vault; + private final AbstractVault vault; - public CryptoMoveV7Feature(final Session session, final Move delegate, final CryptoVault cryptomator) { + public CryptoMoveV7Feature(final Session session, final Move delegate, final AbstractVault 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..f5d6de84717 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoMultipartWriteFeature.java @@ -16,18 +16,18 @@ */ import ch.cyberduck.core.Session; -import ch.cyberduck.core.cryptomator.CryptoVault; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Find; import ch.cyberduck.core.features.MultipartWrite; import ch.cyberduck.core.features.Write; public class CryptoMultipartWriteFeature extends CryptoWriteFeature implements MultipartWrite { - public CryptoMultipartWriteFeature(final Session session, final Write delegate, final CryptoVault vault) { + public CryptoMultipartWriteFeature(final Session session, final Write delegate, final AbstractVault vault) { super(session, delegate, vault); } - public CryptoMultipartWriteFeature(final Session session, final Write delegate, final Find finder, final AttributesFinder attributes, final CryptoVault vault) { + public CryptoMultipartWriteFeature(final Session session, final Write delegate, final Find finder, final AttributesFinder attributes, final AbstractVault vault) { super(session, delegate, vault); } } diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoReadFeature.java index ebfb913d119..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.CryptoVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Read; import ch.cyberduck.core.transfer.TransferStatus; @@ -36,9 +36,9 @@ public class CryptoReadFeature implements Read { private final Session session; private final Read proxy; - private final CryptoVault vault; + private final AbstractVault vault; - public CryptoReadFeature(final Session session, final Read proxy, final CryptoVault vault) { + public CryptoReadFeature(final Session session, final Read proxy, final AbstractVault vault) { this.session = session; this.proxy = proxy; this.vault = vault; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java index 9db63f0ae3d..437a3c0697e 100644 --- a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java +++ b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTimestampFeature.java @@ -17,8 +17,8 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.Session; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.cryptomator.CryptoTransferStatus; -import ch.cyberduck.core.cryptomator.CryptoVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Timestamp; import ch.cyberduck.core.transfer.TransferStatus; @@ -27,9 +27,9 @@ public class CryptoTimestampFeature implements Timestamp { private final Session session; private final Timestamp proxy; - private final CryptoVault vault; + private final AbstractVault vault; - public CryptoTimestampFeature(final Session session, final Timestamp proxy, final CryptoVault vault) { + public CryptoTimestampFeature(final Session session, final Timestamp proxy, final AbstractVault vault) { this.session = session; this.proxy = proxy; this.vault = vault; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java b/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/features/CryptoTouchFeature.java index 4fe570196a1..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.CryptoVault; +import ch.cyberduck.core.cryptomator.AbstractVault; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InvalidFilenameException; @@ -35,9 +35,9 @@ public class CryptoTouchFeature implements Touch { private final Session session; private final Touch proxy; - private final CryptoVault vault; + private final AbstractVault vault; - public CryptoTouchFeature(final Session session, final Touch proxy, final Write writer, final CryptoVault 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 4c677786be3..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.CryptoVault; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Upload; import ch.cyberduck.core.features.Write; @@ -33,9 +33,9 @@ public class CryptoUploadFeature implements Upload { private final Session session; private final Upload proxy; - private final CryptoVault vault; + private final AbstractVault vault; - public CryptoUploadFeature(final Session session, final Upload delegate, final Write writer, final CryptoVault 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 097729d6b7b..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.CryptoVault; import ch.cyberduck.core.cryptomator.random.RandomNonceGenerator; import ch.cyberduck.core.cryptomator.random.RotatingNonceGenerator; import ch.cyberduck.core.exception.BackgroundException; @@ -42,9 +42,9 @@ public class CryptoWriteFeature implements Write { private final Session session; private final Write proxy; - private final CryptoVault vault; + private final AbstractVault vault; - public CryptoWriteFeature(final Session session, final Write proxy, final CryptoVault vault) { + public CryptoWriteFeature(final Session session, final Write proxy, final AbstractVault vault) { this.session = session; this.proxy = proxy; this.vault = vault; diff --git a/cryptomator/src/main/java/ch/cyberduck/core/cryptomator/impl/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/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/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)); 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 dfc8d2e17af..5468f6d459d 100644 --- a/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPCryptomatorInteroperabilityTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/cryptomator/SFTPCryptomatorInteroperabilityTest.java @@ -30,7 +30,6 @@ import ch.cyberduck.core.cryptomator.features.CryptoReadFeature; import ch.cyberduck.core.cryptomator.random.FastSecureRandomProvider; import ch.cyberduck.core.proxy.DisabledProxyFinder; -import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.sftp.SFTPHomeDirectoryService; import ch.cyberduck.core.sftp.SFTPProtocol; import ch.cyberduck.core.sftp.SFTPReadFeature; @@ -56,6 +55,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; @@ -101,7 +101,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);