From 847c6813cc051403de70f06bed799f8801ea1440 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 25 May 2016 15:37:07 +0200 Subject: [PATCH 01/29] started development of 1.2.0 [ci skip] --- main/ant-kit/pom.xml | 2 +- main/commons-test/pom.xml | 2 +- main/commons/pom.xml | 2 +- main/filesystem-api/pom.xml | 2 +- main/filesystem-charsets/pom.xml | 2 +- main/filesystem-crypto-integration-tests/pom.xml | 2 +- main/filesystem-crypto/pom.xml | 2 +- main/filesystem-inmemory/pom.xml | 2 +- main/filesystem-invariants-tests/pom.xml | 2 +- main/filesystem-nameshortening/pom.xml | 2 +- main/filesystem-nio/pom.xml | 2 +- main/filesystem-stats/pom.xml | 2 +- main/frontend-api/pom.xml | 2 +- main/frontend-webdav/pom.xml | 2 +- main/jacoco-report/pom.xml | 2 +- main/pom.xml | 2 +- main/uber-jar/pom.xml | 2 +- main/ui/pom.xml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/main/ant-kit/pom.xml b/main/ant-kit/pom.xml index 3753d71d9..9d43788c7 100644 --- a/main/ant-kit/pom.xml +++ b/main/ant-kit/pom.xml @@ -8,7 +8,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT ant-kit pom diff --git a/main/commons-test/pom.xml b/main/commons-test/pom.xml index 8ae85edf0..53f68521f 100644 --- a/main/commons-test/pom.xml +++ b/main/commons-test/pom.xml @@ -10,7 +10,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT commons-test Cryptomator common test dependencies diff --git a/main/commons/pom.xml b/main/commons/pom.xml index 5603f36b5..afeb35fa1 100644 --- a/main/commons/pom.xml +++ b/main/commons/pom.xml @@ -10,7 +10,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT commons Cryptomator common diff --git a/main/filesystem-api/pom.xml b/main/filesystem-api/pom.xml index c0b4421ce..b4b4cfc4d 100644 --- a/main/filesystem-api/pom.xml +++ b/main/filesystem-api/pom.xml @@ -9,7 +9,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-api Cryptomator filesystem: API diff --git a/main/filesystem-charsets/pom.xml b/main/filesystem-charsets/pom.xml index b5eba0555..49060e25f 100644 --- a/main/filesystem-charsets/pom.xml +++ b/main/filesystem-charsets/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-charsets Cryptomator filesystem: Filename charset compatibility layer diff --git a/main/filesystem-crypto-integration-tests/pom.xml b/main/filesystem-crypto-integration-tests/pom.xml index a5976979c..48b51a55b 100644 --- a/main/filesystem-crypto-integration-tests/pom.xml +++ b/main/filesystem-crypto-integration-tests/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-crypto-integration-tests Cryptomator filesystem: Encryption layer tests diff --git a/main/filesystem-crypto/pom.xml b/main/filesystem-crypto/pom.xml index 3abd9d8b8..1897ef0c9 100644 --- a/main/filesystem-crypto/pom.xml +++ b/main/filesystem-crypto/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-crypto Cryptomator filesystem: Encryption layer diff --git a/main/filesystem-inmemory/pom.xml b/main/filesystem-inmemory/pom.xml index 6134992dc..d1db21e24 100644 --- a/main/filesystem-inmemory/pom.xml +++ b/main/filesystem-inmemory/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-inmemory Cryptomator filesystem: In-memory mock diff --git a/main/filesystem-invariants-tests/pom.xml b/main/filesystem-invariants-tests/pom.xml index 054464338..83ec41625 100644 --- a/main/filesystem-invariants-tests/pom.xml +++ b/main/filesystem-invariants-tests/pom.xml @@ -9,7 +9,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-invariants-tests Cryptomator filesystem: Invariants tests diff --git a/main/filesystem-nameshortening/pom.xml b/main/filesystem-nameshortening/pom.xml index d5830d78d..f325b84c3 100644 --- a/main/filesystem-nameshortening/pom.xml +++ b/main/filesystem-nameshortening/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-nameshortening Cryptomator filesystem: Name shortening layer diff --git a/main/filesystem-nio/pom.xml b/main/filesystem-nio/pom.xml index f091d5fcb..4f535b1b0 100644 --- a/main/filesystem-nio/pom.xml +++ b/main/filesystem-nio/pom.xml @@ -7,7 +7,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-nio Cryptomator filesystem: NIO-based physical layer diff --git a/main/filesystem-stats/pom.xml b/main/filesystem-stats/pom.xml index 764daf84f..4912e2a72 100644 --- a/main/filesystem-stats/pom.xml +++ b/main/filesystem-stats/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT filesystem-stats Cryptomator filesystem: Throughput statistics diff --git a/main/frontend-api/pom.xml b/main/frontend-api/pom.xml index 875e85f95..7952ce438 100644 --- a/main/frontend-api/pom.xml +++ b/main/frontend-api/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT frontend-api Cryptomator frontend: API diff --git a/main/frontend-webdav/pom.xml b/main/frontend-webdav/pom.xml index 64de5fea5..1e6421508 100644 --- a/main/frontend-webdav/pom.xml +++ b/main/frontend-webdav/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT frontend-webdav Cryptomator frontend: WebDAV frontend diff --git a/main/jacoco-report/pom.xml b/main/jacoco-report/pom.xml index 20de8d2bf..b57576813 100644 --- a/main/jacoco-report/pom.xml +++ b/main/jacoco-report/pom.xml @@ -5,7 +5,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT jacoco-report Cryptomator Code Coverage Report diff --git a/main/pom.xml b/main/pom.xml index 75ba59663..ed4978989 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -7,7 +7,7 @@ 4.0.0 org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT pom Cryptomator diff --git a/main/uber-jar/pom.xml b/main/uber-jar/pom.xml index 833e1b3ea..dc7a961fd 100644 --- a/main/uber-jar/pom.xml +++ b/main/uber-jar/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT uber-jar pom diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 873c1af38..2c261d3ad 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -12,7 +12,7 @@ org.cryptomator main - 1.1.0-SNAPSHOT + 1.2.0-SNAPSHOT ui Cryptomator GUI From 1804b98f057994bf39ccb512b6e2d238e8037b9c Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 25 May 2016 15:47:19 +0200 Subject: [PATCH 02/29] trigger coverity scans for only release branches [ci skip] --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 611045f47..3016c1cb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ addons: name: "cryptomator/cryptomator" notification_email: sebastian.stenzel@cryptomator.org build_command: "mvn -fmain/pom.xml clean test -DskipTests" - branch_pattern: coverity_scan + branch_pattern: release.* deploy: provider: releases From 87ff33956b5752f7bf9d95ca9e5f2173d2b5c654 Mon Sep 17 00:00:00 2001 From: Aeris Date: Mon, 30 May 2016 13:45:40 +0200 Subject: [PATCH 03/29] Enhanced fr translation --- .../ui/src/main/resources/localization/fr.txt | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/main/ui/src/main/resources/localization/fr.txt b/main/ui/src/main/resources/localization/fr.txt index 17aec69f4..825a49d74 100644 --- a/main/ui/src/main/resources/localization/fr.txt +++ b/main/ui/src/main/resources/localization/fr.txt @@ -15,16 +15,16 @@ main.directoryList.contextMenu.changePassword = Changer le mot de passe main.addDirectory.contextMenu.new = Créer un nouveau coffre main.addDirectory.contextMenu.open = Ouvrir un coffre existant # welcome.fxml -welcome.checkForUpdates.label.currentlyChecking = Recherche de mise à jour... -welcome.newVersionMessage = La version %s peut-être téléchargée. Il s'agit de %s. +welcome.checkForUpdates.label.currentlyChecking = Recherche de mise à jour… +welcome.newVersionMessage = La version %s peut-être téléchargée. Il s’agit de %s. # initialize.fxml initialize.label.password = Mot de passe initialize.label.retypePassword = Confirmation initialize.button.ok = Créer le coffre initialize.messageLabel.alreadyInitialized = Coffre déjà initialisé -initialize.messageLabel.initializationFailed = Impossible d'initialiser le coffre. Voir le fichier de log pour plus de détails. +initialize.messageLabel.initializationFailed = Impossible d’initialiser le coffre. Voir le fichier de log pour plus de détails. # notfound.fxml -notfound.label = Coffre introuvable. A t'il été déplacé? +notfound.label = Coffre introuvable. A-t-il été déplacé ? # upgrade.fxml upgrade.button = Mettre à niveau upgrade.version3dropBundleExtension.msg = Ce coffre doit être converti dans un format plus récent.\n"%1$s" sera renommé en "%2$s".\nAssurez-vous que la synchronisation est terminée avant de continuer. @@ -36,15 +36,15 @@ unlock.label.winDriveLetter = Lettre du lecteur unlock.label.downloadsPageLink = Toutes les versions de Cryptomator unlock.label.advancedHeading = Options avancées unlock.button.unlock = Déverrouiller le coffre -unlock.button.advancedOptions.show = Plus d'options -unlock.button.advancedOptions.hide = Moins d'options +unlock.button.advancedOptions.show = Plus d’options +unlock.button.advancedOptions.hide = Moins d’options unlock.choicebox.winDriveLetter.auto = Assigner automatiquement unlock.errorMessage.wrongPassword = Mot de passe incorrect -unlock.errorMessage.mountingFailed = Echec du montage. Voir le fichier de log pour plus de détails. -unlock.errorMessage.unsupportedKeyLengthInstallJCE = Echec du décryptage. Veuillez installer la Policy Oracle "JCE Unlimited Strength Policy". +unlock.errorMessage.mountingFailed = Échec du montage. Voir le fichier de log pour plus de détails. +unlock.errorMessage.unsupportedKeyLengthInstallJCE = Échec du déchiffrement. Veuillez installer la Policy Oracle "JCE Unlimited Strength Policy". unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Coffre non supporté. Ce coffre a été créé avec une ancienne version de Cryptomator. unlock.errorMessage.unsupportedVersion.softwareOlderThanVault = Coffre non supporté. Ce coffre a été créé avec une version de Cryptomator plus récente. -unlock.messageLabel.startServerFailed = Le serveur WebDAV n'a pas pu démarrer. +unlock.messageLabel.startServerFailed = Le serveur WebDAV n’a pas pu démarrer. # change_password.fxml changePassword.label.oldPassword = Ancien mot de passe changePassword.label.newPassword = Nouveau mot de passe @@ -52,35 +52,35 @@ changePassword.label.retypePassword = Vérification changePassword.label.downloadsPageLink = Toutes les versions de Cryptomator changePassword.button.change = Modification du mot de masse changePassword.errorMessage.wrongPassword = Mot de passe incorrect -changePassword.errorMessage.decryptionFailed = Echec du décryptage -changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Echec du décryptage. Veuillez installer la Policy Oracle "JCE Unlimited Strength Policy". +changePassword.errorMessage.decryptionFailed = Échec du déchiffrement +changePassword.errorMessage.unsupportedKeyLengthInstallJCE = Échec du déchiffrement. Veuillez installer la Policy Oracle "JCE Unlimited Strength Policy". changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware = Coffre non supporté. Ce coffre a été créé avec une ancienne version de Cryptomator. changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault = Coffre non supporté. Ce coffre a été créé avec une version de Cryptomator plus récente. -changePassword.infoMessage.success = Password changed +changePassword.infoMessage.success = Mot de passe modifié # unlocked.fxml unlocked.button.lock = Verrouiller le coffre unlocked.moreOptions.reveal = Voir le lecteur -unlocked.moreOptions.copyUrl = Copier l'URL WebDAV -unlocked.label.revealFailed = Echec de la commande -unlocked.label.unmountFailed = Echec de l'éjection du lecteur -unlocked.label.statsEncrypted = crypté -unlocked.label.statsDecrypted = décryptage +unlocked.moreOptions.copyUrl = Copier l’URL WebDAV +unlocked.label.revealFailed = Échec de la commande +unlocked.label.unmountFailed = Échec de l’éjection du lecteur +unlocked.label.statsEncrypted = chiffrement +unlocked.label.statsDecrypted = déchiffrement unlocked.ioGraph.yAxis.label = Débit (MiB/s) # mac_warnings.fxml macWarnings.windowTitle = Attention - Fichier corrompu dans %s macWarnings.message = Cryptomator a détecté des corruptions de données dans les fichiers suivants\: macWarnings.moreInformationButton = En savoir plus -macWarnings.whitelistButton = Décrypter tout de même +macWarnings.whitelistButton = Déchiffrer tout de même # settings.fxml settings.version.label = Version %s settings.checkForUpdates.label = Vérif. des mises à jour settings.port.label = Port WebDAV * settings.port.prompt = 0 \= Choix automatique -settings.useipv6.label = Utiliser un litéral IPv6 +settings.useipv6.label = Utiliser un littéral IPv6 settings.requiresRestartLabel = * Redémarrage requis # tray icon tray.menu.open = Ouvrir tray.menu.quit = Quitter tray.infoMsg.title = Toujours en fonctionnement -tray.infoMsg.msg = Cryptomator est toujours en fonctionnement. Utiliser l'icône de la barre des tâches pour quitter. +tray.infoMsg.msg = Cryptomator est toujours en fonctionnement. Utilisez l’icône de la barre des tâches pour quitter. tray.infoMsg.msg.osx = Cryptomator est toujours en fonctionnement. Utilisez la barre de menu pour quitter. From 1d9252e974322a5d0a8f660339181417729faa21 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Tue, 7 Jun 2016 01:07:54 +0200 Subject: [PATCH 04/29] updated description of file chooser's extension filter [ci skip] --- .../java/org/cryptomator/ui/controllers/MainController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index 810da32eb..b74b88e80 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -202,7 +202,7 @@ public class MainController extends LocalizedFXMLViewController { @FXML private void didClickAddExistingVaults(ActionEvent event) { final FileChooser fileChooser = new FileChooser(); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION)); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*" + Vault.VAULT_FILE_EXTENSION)); final List files = fileChooser.showOpenMultipleDialog(mainWindow); if (files != null) { for (final File file : files) { From 41f8a9facaade64547b193393a7a2b73227ec37a Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 8 Jun 2016 19:06:06 +0200 Subject: [PATCH 05/29] add "allow" response header field --- .../frontend/webdav/WindowsCompatibilityServlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WindowsCompatibilityServlet.java b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WindowsCompatibilityServlet.java index 14443ea22..0edfc92f8 100644 --- a/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WindowsCompatibilityServlet.java +++ b/main/frontend-webdav/src/main/java/org/cryptomator/frontend/webdav/WindowsCompatibilityServlet.java @@ -32,7 +32,7 @@ public class WindowsCompatibilityServlet extends HttpServlet { protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.addHeader("DAV", "1, 2"); resp.addHeader("MS-Author-Via", "DAV"); - // resp.addHeader("Allow", "OPTIONS, GET, HEAD, POST, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, PUT, DELETE, MOVE, LOCK, UNLOCK"); + resp.addHeader("Allow", "OPTIONS, GET, HEAD"); resp.setStatus(HttpServletResponse.SC_NO_CONTENT); } From e2b94ff6ef7dbe7fb4d4d4fdc0ba3637c41a9ac5 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Wed, 8 Jun 2016 19:11:56 +0200 Subject: [PATCH 06/29] updated jacoco dependency --- main/pom.xml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/main/pom.xml b/main/pom.xml index ed4978989..321c0122e 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -49,16 +49,6 @@ https://jitpack.io - - - - jacoco-snapshots - https://oss.sonatype.org/content/repositories/snapshots - - true - - - @@ -332,7 +322,7 @@ org.jacoco jacoco-maven-plugin - 0.7.7-SNAPSHOT + 0.7.7.201606060606 prepare-agent From 414bbef1a74b38f0aa5547cb82639cc1de1ea4c0 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 10 Jun 2016 14:04:55 +0200 Subject: [PATCH 07/29] updated key generation --- .../crypto/engine/impl/CryptorImpl.java | 28 ++++++++++--------- .../crypto/engine/impl/FileHeaderPayload.java | 14 ++++++---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java index b0b90cdbe..ef88c4a8d 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java @@ -18,6 +18,7 @@ import java.security.SecureRandom; import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; +import javax.crypto.KeyGenerator; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -81,14 +82,15 @@ class CryptorImpl implements Cryptor { @Override public void randomizeMasterkey() { - final byte[] randomBytes = new byte[KEYLENGTH_IN_BYTES]; try { - randomSource.nextBytes(randomBytes); - encryptionKey = new SecretKeySpec(randomBytes, ENCRYPTION_ALG); - randomSource.nextBytes(randomBytes); - macKey = new SecretKeySpec(randomBytes, MAC_ALG); - } finally { - Arrays.fill(randomBytes, (byte) 0x00); + KeyGenerator encKeyGen = KeyGenerator.getInstance(ENCRYPTION_ALG); + encKeyGen.init(KEYLENGTH_IN_BYTES * Byte.SIZE, randomSource); + encryptionKey = encKeyGen.generateKey(); + KeyGenerator macKeyGen = KeyGenerator.getInstance(MAC_ALG); + macKeyGen.init(KEYLENGTH_IN_BYTES * Byte.SIZE, randomSource); + macKey = macKeyGen.generateKey(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Hard-coded algorithm doesn't exist.", e); } } @@ -116,12 +118,12 @@ class CryptorImpl implements Cryptor { final SecretKey kek = new SecretKeySpec(kekBytes, ENCRYPTION_ALG); this.macKey = AesKeyWrap.unwrap(kek, keyFile.getMacMasterKey(), MAC_ALG); // future use (as soon as we need to prevent downgrade attacks): -// final Mac mac = new ThreadLocalMac(macKey, MAC_ALG).get(); -// final byte[] versionMac = mac.doFinal(ByteBuffer.allocate(Integer.BYTES).putInt(CURRENT_VAULT_VERSION).array()); -// if (!MessageDigest.isEqual(versionMac, keyFile.getVersionMac())) { -// destroyQuietly(macKey); -// throw new UnsupportedVaultFormatException(Integer.MAX_VALUE, CURRENT_VAULT_VERSION); -// } + // final Mac mac = new ThreadLocalMac(macKey, MAC_ALG).get(); + // final byte[] versionMac = mac.doFinal(ByteBuffer.allocate(Integer.BYTES).putInt(CURRENT_VAULT_VERSION).array()); + // if (!MessageDigest.isEqual(versionMac, keyFile.getVersionMac())) { + // destroyQuietly(macKey); + // throw new UnsupportedVaultFormatException(Integer.MAX_VALUE, CURRENT_VAULT_VERSION); + // } this.encryptionKey = AesKeyWrap.unwrap(kek, keyFile.getEncryptionMasterKey(), ENCRYPTION_ALG); } catch (InvalidKeyException e) { throw new InvalidPassphraseException(); diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileHeaderPayload.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileHeaderPayload.java index 229443e8c..2016dbfa1 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileHeaderPayload.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileHeaderPayload.java @@ -11,12 +11,14 @@ package org.cryptomator.crypto.engine.impl; import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; @@ -36,13 +38,13 @@ class FileHeaderPayload implements Destroyable { private final SecretKey contentKey; public FileHeaderPayload(SecureRandom randomSource) { - filesize = 0; - final byte[] contentKey = new byte[CONTENT_KEY_LEN]; + this.filesize = 0; try { - randomSource.nextBytes(contentKey); - this.contentKey = new SecretKeySpec(contentKey, AES); - } finally { - Arrays.fill(contentKey, (byte) 0x00); + KeyGenerator keyGen = KeyGenerator.getInstance(AES); + keyGen.init(CONTENT_KEY_LEN * Byte.SIZE, randomSource); + this.contentKey = keyGen.generateKey(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Hard-coded algorithm doesn't exist.", e); } } From 07ba4eb53768b5a3f87028df07719f687ede92b6 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 30 Jun 2016 18:02:13 +0200 Subject: [PATCH 08/29] Using 0 prefix instead of _ suffix to mark directories --- .../crypto/engine/impl/Constants.java | 2 +- .../engine/impl/FilenameCryptorImpl.java | 3 ++- .../filesystem/crypto/ConflictResolver.java | 10 +++++----- .../filesystem/crypto/Constants.java | 2 +- .../filesystem/crypto/CryptoFolder.java | 18 +++++++++--------- .../crypto/ConflictResolverTest.java | 3 ++- 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java index dc0f0c9d4..262e1d1a1 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java @@ -5,7 +5,7 @@ public final class Constants { private Constants() { } - static final Integer CURRENT_VAULT_VERSION = 3; + static final Integer CURRENT_VAULT_VERSION = 4; public static final int PAYLOAD_SIZE = 32 * 1024; public static final int NONCE_SIZE = 16; diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java index 4b977d44d..6833d1b9a 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FilenameCryptorImpl.java @@ -26,7 +26,8 @@ import org.cryptomator.siv.SivMode; class FilenameCryptorImpl implements FilenameCryptor { private static final BaseNCodec BASE32 = new Base32(); - private static final Pattern BASE32_PATTERN = Pattern.compile("([A-Z0-9]{8})*[A-Z0-9=]{8}"); + // https://tools.ietf.org/html/rfc4648#section-6 + private static final Pattern BASE32_PATTERN = Pattern.compile("([A-Z2-7]{8})*[A-Z2-7=]{8}"); private static final ThreadLocal SHA1 = new ThreadLocalSha1(); private static final ThreadLocal AES_SIV = new ThreadLocal() { @Override diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java index 11bc14f77..19dceb326 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/ConflictResolver.java @@ -1,6 +1,6 @@ package org.cryptomator.filesystem.crypto; -import static org.cryptomator.filesystem.crypto.Constants.DIR_SUFFIX; +import static org.cryptomator.filesystem.crypto.Constants.DIR_PREFIX; import java.util.Optional; import java.util.UUID; @@ -32,7 +32,7 @@ final class ConflictResolver { } public File resolveIfNecessary(File file) { - Matcher m = encryptedNamePattern.matcher(StringUtils.removeEnd(file.name(), DIR_SUFFIX)); + Matcher m = encryptedNamePattern.matcher(StringUtils.removeStart(file.name(), DIR_PREFIX)); if (m.matches()) { // full match, use file as is return file; @@ -47,11 +47,11 @@ final class ConflictResolver { private File resolveConflict(File conflictingFile, MatchResult matchResult) { String ciphertext = matchResult.group(); - boolean isDirectory = conflictingFile.name().substring(matchResult.end()).startsWith(DIR_SUFFIX); + boolean isDirectory = conflictingFile.name().startsWith(DIR_PREFIX); Optional cleartext = nameDecryptor.apply(ciphertext); if (cleartext.isPresent()) { Folder folder = conflictingFile.parent().get(); - File canonicalFile = folder.file(isDirectory ? ciphertext + DIR_SUFFIX : ciphertext); + File canonicalFile = folder.file(isDirectory ? DIR_PREFIX + ciphertext : ciphertext); if (canonicalFile.exists()) { // there must not be two directories pointing to the same directory id. In this case no human interaction is needed to resolve this conflict: if (isDirectory && FileContents.UTF_8.readContents(canonicalFile).equals(FileContents.UTF_8.readContents(conflictingFile))) { @@ -66,7 +66,7 @@ final class ConflictResolver { conflictId = createConflictId(); String alternativeCleartext = cleartext.get() + " (Conflict " + conflictId + ")"; String alternativeCiphertext = nameEncryptor.apply(alternativeCleartext).get(); - alternativeFile = folder.file(isDirectory ? alternativeCiphertext + DIR_SUFFIX : alternativeCiphertext); + alternativeFile = folder.file(isDirectory ? DIR_PREFIX + alternativeCiphertext : alternativeCiphertext); } while (alternativeFile.exists()); LOG.info("Detected conflict {}:\n{}\n{}", conflictId, canonicalFile, conflictingFile); conflictingFile.moveTo(alternativeFile); diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java index 7e6d31af0..78a745b71 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java @@ -11,6 +11,6 @@ public final class Constants { static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup"; - static final String DIR_SUFFIX = "_"; + static final String DIR_PREFIX = "0"; } diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java index 10e4289a6..b66721ca2 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/CryptoFolder.java @@ -10,7 +10,7 @@ package org.cryptomator.filesystem.crypto; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.cryptomator.filesystem.crypto.Constants.DIR_SUFFIX; +import static org.cryptomator.filesystem.crypto.Constants.DIR_PREFIX; import java.io.FileNotFoundException; import java.io.UncheckedIOException; @@ -54,9 +54,9 @@ class CryptoFolder extends CryptoNode implements Folder { @Override protected Optional encryptedName() { if (parent().isPresent()) { - return parent().get().encryptChildName(name()).map(s -> s + DIR_SUFFIX); + return parent().get().encryptChildName(name()).map(s -> DIR_PREFIX + s); } else { - return Optional.of(cryptor.getFilenameCryptor().encryptFilename(name()) + DIR_SUFFIX); + return Optional.of(DIR_PREFIX + cryptor.getFilenameCryptor().encryptFilename(name())); } } @@ -121,20 +121,20 @@ class CryptoFolder extends CryptoNode implements Folder { @Override public Stream files() { - return nonConflictingFiles().map(File::name).filter(endsWithDirSuffix().negate()).map(this::decryptChildName).filter(Optional::isPresent).map(Optional::get).map(this::file); + return nonConflictingFiles().map(File::name).filter(startsWithDirPrefix().negate()).map(this::decryptChildName).filter(Optional::isPresent).map(Optional::get).map(this::file); } @Override public Stream folders() { - return nonConflictingFiles().map(File::name).filter(endsWithDirSuffix()).map(this::removeDirSuffix).map(this::decryptChildName).filter(Optional::isPresent).map(Optional::get).map(this::folder); + return nonConflictingFiles().map(File::name).filter(startsWithDirPrefix()).map(this::removeDirPrefix).map(this::decryptChildName).filter(Optional::isPresent).map(Optional::get).map(this::folder); } - private Predicate endsWithDirSuffix() { - return (String encryptedFolderName) -> StringUtils.endsWith(encryptedFolderName, DIR_SUFFIX); + private Predicate startsWithDirPrefix() { + return (String encryptedFolderName) -> StringUtils.startsWith(encryptedFolderName, DIR_PREFIX); } - private String removeDirSuffix(String encryptedFolderName) { - return StringUtils.removeEnd(encryptedFolderName, DIR_SUFFIX); + private String removeDirPrefix(String encryptedFolderName) { + return StringUtils.removeStart(encryptedFolderName, DIR_PREFIX); } @Override diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/ConflictResolverTest.java b/main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/ConflictResolverTest.java index b99a000b4..bd1cf3b51 100644 --- a/main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/ConflictResolverTest.java +++ b/main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/crypto/ConflictResolverTest.java @@ -46,7 +46,7 @@ public class ConflictResolverTest { unrelatedFile = Mockito.mock(File.class); String canonicalFileName = encode.apply("test name").get(); - String canonicalFolderName = canonicalFileName + Constants.DIR_SUFFIX; + String canonicalFolderName = Constants.DIR_PREFIX + canonicalFileName; String conflictingFileName = canonicalFileName + " (version 2)"; String conflictingFolderName = canonicalFolderName + " (version 2)"; String unrelatedName = "notBa$e32!"; @@ -70,6 +70,7 @@ public class ConflictResolverTest { Mockito.doReturn(Optional.of(folder)).when(unrelatedFile).parent(); Mockito.when(folder.file(Mockito.startsWith(canonicalFileName.substring(0, 8)))).thenReturn(resolved); + Mockito.when(folder.file(Mockito.startsWith(canonicalFolderName.substring(0, 8)))).thenReturn(resolved); Mockito.when(folder.file(canonicalFileName)).thenReturn(canonicalFile); Mockito.when(folder.file(canonicalFolderName)).thenReturn(canonicalFolder); Mockito.when(folder.file(conflictingFileName)).thenReturn(conflictingFile); From 1468c6ec905a02e03feefd0abac8be5c585d1e73 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 30 Jun 2016 22:09:45 +0200 Subject: [PATCH 09/29] improved vault upgrading, preparation for migration to vault version 4 --- .../UnsupportedVaultFormatException.java | 16 ++-- .../crypto/engine/impl/Constants.java | 5 ++ .../crypto/engine/impl/CryptorImpl.java | 3 +- .../filesystem/crypto/Constants.java | 4 +- .../ui/controllers/MainController.java | 7 +- .../ui/controllers/UpgradeController.java | 58 ++++++++----- .../ui/model/UpgradeInstruction.java | 40 --------- .../ui/model/UpgradeStrategies.java | 27 ++++++ .../cryptomator/ui/model/UpgradeStrategy.java | 87 +++++++++++++++++++ .../UpgradeVersion3DropBundleExtension.java | 43 ++++++++- .../ui/model/UpgradeVersion3to4.java | 55 ++++++++++++ .../java/org/cryptomator/ui/model/Vault.java | 15 +--- main/ui/src/main/resources/fxml/upgrade.fxml | 3 + .../ui/src/main/resources/localization/en.txt | 3 + 14 files changed, 277 insertions(+), 89 deletions(-) delete mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java create mode 100644 main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/UnsupportedVaultFormatException.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/UnsupportedVaultFormatException.java index b3d06b9f2..1f087b080 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/UnsupportedVaultFormatException.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/UnsupportedVaultFormatException.java @@ -11,28 +11,28 @@ package org.cryptomator.crypto.engine; public class UnsupportedVaultFormatException extends CryptoException { private final Integer detectedVersion; - private final Integer supportedVersion; + private final Integer latestSupportedVersion; - public UnsupportedVaultFormatException(Integer detectedVersion, Integer supportedVersion) { - super("Tried to open vault of version " + detectedVersion + ", but can only handle version " + supportedVersion); + public UnsupportedVaultFormatException(Integer detectedVersion, Integer latestSupportedVersion) { + super("Tried to open vault of version " + detectedVersion + ", latest supported version is " + latestSupportedVersion); this.detectedVersion = detectedVersion; - this.supportedVersion = supportedVersion; + this.latestSupportedVersion = latestSupportedVersion; } public Integer getDetectedVersion() { return detectedVersion; } - public Integer getSupportedVersion() { - return supportedVersion; + public Integer getLatestSupportedVersion() { + return latestSupportedVersion; } public boolean isVaultOlderThanSoftware() { - return detectedVersion == null || detectedVersion < supportedVersion; + return detectedVersion == null || detectedVersion < latestSupportedVersion; } public boolean isSoftwareOlderThanVault() { - return detectedVersion > supportedVersion; + return detectedVersion > latestSupportedVersion; } } diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java index 262e1d1a1..efbe84bfc 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/Constants.java @@ -1,10 +1,15 @@ package org.cryptomator.crypto.engine.impl; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + public final class Constants { private Constants() { } + static final Collection SUPPORTED_VAULT_VERSIONS = Collections.unmodifiableCollection(Arrays.asList(3, 4)); static final Integer CURRENT_VAULT_VERSION = 4; public static final int PAYLOAD_SIZE = 32 * 1024; diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java index ef88c4a8d..836e6c134 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/CryptorImpl.java @@ -9,6 +9,7 @@ package org.cryptomator.crypto.engine.impl; import static org.cryptomator.crypto.engine.impl.Constants.CURRENT_VAULT_VERSION; +import static org.cryptomator.crypto.engine.impl.Constants.SUPPORTED_VAULT_VERSIONS; import java.io.IOException; import java.nio.ByteBuffer; @@ -109,7 +110,7 @@ class CryptorImpl implements Cryptor { assert keyFile != null; // check version - if (!CURRENT_VAULT_VERSION.equals(keyFile.getVersion())) { + if (!SUPPORTED_VAULT_VERSIONS.contains(keyFile.getVersion())) { throw new UnsupportedVaultFormatException(keyFile.getVersion(), CURRENT_VAULT_VERSION); } diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java index 78a745b71..eb552aad7 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/crypto/Constants.java @@ -8,8 +8,8 @@ public final class Constants { static final String DATA_ROOT_DIR = "d"; static final String ROOT_DIRECOTRY_ID = ""; - static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; - static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup"; + public static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; + public static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup"; static final String DIR_PREFIX = "0"; diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java index b74b88e80..c9dee3248 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java @@ -26,6 +26,7 @@ import javax.inject.Singleton; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.ui.controls.DirectoryListCell; +import org.cryptomator.ui.model.UpgradeStrategies; import org.cryptomator.ui.model.Vault; import org.cryptomator.ui.model.VaultFactory; import org.cryptomator.ui.settings.Localization; @@ -78,6 +79,7 @@ public class MainController extends LocalizedFXMLViewController { private final Provider unlockedControllerProvider; private final Lazy changePasswordController; private final Lazy settingsController; + private final Lazy upgradeStrategies; private final ObjectProperty activeController = new SimpleObjectProperty<>(); private final ObservableList vaults; private final ObjectProperty selectedVault = new SimpleObjectProperty<>(); @@ -90,7 +92,7 @@ public class MainController extends LocalizedFXMLViewController { @Inject public MainController(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, VaultFactory vaultFactoy, Lazy welcomeController, Lazy initializeController, Lazy notFoundController, Lazy upgradeController, Lazy unlockController, - Provider unlockedControllerProvider, Lazy changePasswordController, Lazy settingsController) { + Provider unlockedControllerProvider, Lazy changePasswordController, Lazy settingsController, Lazy upgradeStrategies) { super(localization); this.mainWindow = mainWindow; this.vaultFactoy = vaultFactoy; @@ -102,6 +104,7 @@ public class MainController extends LocalizedFXMLViewController { this.unlockedControllerProvider = unlockedControllerProvider; this.changePasswordController = changePasswordController; this.settingsController = settingsController; + this.upgradeStrategies = upgradeStrategies; this.vaults = FXCollections.observableList(settings.getDirectories()); this.vaults.addListener((Change c) -> { settings.save(); @@ -288,7 +291,7 @@ public class MainController extends LocalizedFXMLViewController { this.showUnlockedView(newValue); } else if (!newValue.doesVaultDirectoryExist()) { this.showNotFoundView(); - } else if (newValue.isValidVaultDirectory() && newValue.needsUpgrade()) { + } else if (newValue.isValidVaultDirectory() && upgradeStrategies.get().getUpgradeStrategy(newValue).isPresent()) { this.showUpgradeView(); } else if (newValue.isValidVaultDirectory()) { this.showUnlockView(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java index f559fa50d..73b0e8361 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java @@ -7,8 +7,10 @@ import java.util.concurrent.ExecutorService; import javax.inject.Inject; -import org.cryptomator.ui.model.UpgradeInstruction; -import org.cryptomator.ui.model.UpgradeInstruction.UpgradeFailedException; +import org.cryptomator.ui.controls.SecPasswordField; +import org.cryptomator.ui.model.UpgradeStrategies; +import org.cryptomator.ui.model.UpgradeStrategy; +import org.cryptomator.ui.model.UpgradeStrategy.UpgradeFailedException; import org.cryptomator.ui.model.Vault; import org.cryptomator.ui.settings.Localization; import org.fxmisc.easybind.EasyBind; @@ -16,7 +18,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javafx.application.Platform; -import javafx.beans.binding.Binding; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.event.ActionEvent; @@ -30,19 +31,24 @@ public class UpgradeController extends LocalizedFXMLViewController { private static final Logger LOG = LoggerFactory.getLogger(UpgradeController.class); final ObjectProperty vault = new SimpleObjectProperty<>(); + final ObjectProperty> strategy = new SimpleObjectProperty<>(); + private final UpgradeStrategies strategies; private final ExecutorService exec; - private final Binding> upgradeInstruction = EasyBind.monadic(vault).map(Vault::availableUpgrade); private Optional listener = Optional.empty(); @Inject - public UpgradeController(Localization localization, ExecutorService exec) { + public UpgradeController(Localization localization, UpgradeStrategies strategies, ExecutorService exec) { super(localization); + this.strategies = strategies; this.exec = exec; } @FXML private Label upgradeLabel; + @FXML + private SecPasswordField passwordField; + @FXML private Button upgradeButton; @@ -54,10 +60,12 @@ public class UpgradeController extends LocalizedFXMLViewController { @Override protected void initialize() { - upgradeLabel.textProperty().bind(EasyBind.monadic(upgradeInstruction).map(instruction -> { + upgradeLabel.textProperty().bind(EasyBind.monadic(strategy).map(instruction -> { return instruction.map(this::upgradeNotification).orElse(""); }).orElse("")); + upgradeButton.disableProperty().bind(passwordField.textProperty().isEmpty().or(passwordField.disabledProperty())); + EasyBind.subscribe(vault, this::vaultDidChange); } @@ -68,14 +76,15 @@ public class UpgradeController extends LocalizedFXMLViewController { private void vaultDidChange(Vault newVault) { errorLabel.setText(null); + strategy.set(strategies.getUpgradeStrategy(newVault)); } // **************************************** // Upgrade label // **************************************** - private String upgradeNotification(UpgradeInstruction instruction) { - return instruction.getNotification(vault.get(), localization); + private String upgradeNotification(UpgradeStrategy instruction) { + return instruction.getNotification(vault.get()); } // **************************************** @@ -84,36 +93,45 @@ public class UpgradeController extends LocalizedFXMLViewController { @FXML private void didClickUpgradeButton(ActionEvent event) { - upgradeInstruction.getValue().ifPresent(this::upgrade); + strategy.getValue().ifPresent(this::upgrade); } - private void upgrade(UpgradeInstruction instruction) { - Vault v = vault.getValue(); - Objects.requireNonNull(v); + private void upgrade(UpgradeStrategy instruction) { + Vault v = Objects.requireNonNull(vault.getValue()); + passwordField.setDisable(true); progressIndicator.setVisible(true); - upgradeButton.setDisable(true); exec.submit(() -> { if (!instruction.isApplicable(v)) { LOG.error("No upgrade needed for " + v.path().getValue()); throw new IllegalStateException("No ugprade needed for " + v.path().getValue()); } try { - instruction.upgrade(v, localization); - Platform.runLater(() -> { - progressIndicator.setVisible(false); - upgradeButton.setDisable(false); - listener.ifPresent(UpgradeListener::didUpgrade); - }); + instruction.upgrade(v, passwordField.getCharacters()); + Platform.runLater(this::showNextUpgrade); } catch (UpgradeFailedException e) { Platform.runLater(() -> { errorLabel.setText(e.getLocalizedMessage()); + }); + } finally { + Platform.runLater(() -> { progressIndicator.setVisible(false); - upgradeButton.setDisable(false); + passwordField.setDisable(false); + passwordField.swipe(); }); } }); } + private void showNextUpgrade() { + errorLabel.setText(null); + Optional nextStrategy = strategies.getUpgradeStrategy(vault.getValue()); + if (nextStrategy.isPresent()) { + strategy.set(nextStrategy); + } else { + listener.ifPresent(UpgradeListener::didUpgrade); + } + } + /* callback */ public void setListener(UpgradeListener listener) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java deleted file mode 100644 index a903c1369..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeInstruction.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.cryptomator.ui.model; - -import org.cryptomator.ui.settings.Localization; - -public interface UpgradeInstruction { - - static UpgradeInstruction[] AVAILABLE_INSTRUCTIONS = {new UpgradeVersion3DropBundleExtension()}; - - /** - * @return Localized string to display to the user when an upgrade is needed. - */ - String getNotification(Vault vault, Localization localization); - - /** - * Upgrades a vault. Might take a moment, should be run in a background thread. - */ - void upgrade(Vault vault, Localization localization) throws UpgradeFailedException; - - /** - * Determines in O(1), if an upgrade can be applied to a vault. - * - * @return true if and only if the vault can be migrated to a newer version without the risk of data losses. - */ - boolean isApplicable(Vault vault); - - /** - * Thrown when data migration failed. - */ - public class UpgradeFailedException extends Exception { - - UpgradeFailedException() { - } - - UpgradeFailedException(String message) { - super(message); - } - - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java new file mode 100644 index 000000000..8a8ec3444 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategies.java @@ -0,0 +1,27 @@ +package org.cryptomator.ui.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class UpgradeStrategies { + + private final Collection strategies; + + @Inject + public UpgradeStrategies(UpgradeVersion3DropBundleExtension upgrader1, UpgradeVersion3to4 upgrader2) { + strategies = Collections.unmodifiableList(Arrays.asList(upgrader1, upgrader2)); + } + + public Optional getUpgradeStrategy(Vault vault) { + return strategies.stream().filter(strategy -> { + return strategy.isApplicable(vault); + }).findFirst(); + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java new file mode 100644 index 000000000..6f7f147ce --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeStrategy.java @@ -0,0 +1,87 @@ +package org.cryptomator.ui.model; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; + +import javax.inject.Provider; + +import org.cryptomator.crypto.engine.Cryptor; +import org.cryptomator.crypto.engine.InvalidPassphraseException; +import org.cryptomator.crypto.engine.UnsupportedVaultFormatException; +import org.cryptomator.filesystem.crypto.Constants; +import org.cryptomator.ui.settings.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class UpgradeStrategy { + + private static final Logger LOG = LoggerFactory.getLogger(UpgradeStrategy.class); + + protected final Provider cryptorProvider; + protected final Localization localization; + + UpgradeStrategy(Provider cryptorProvider, Localization localization) { + this.cryptorProvider = cryptorProvider; + this.localization = localization; + } + + /** + * @return Localized string to display to the user when an upgrade is needed. + */ + public abstract String getNotification(Vault vault); + + /** + * Upgrades a vault. Might take a moment, should be run in a background thread. + */ + public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException { + final Cryptor cryptor = cryptorProvider.get(); + try { + final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); + final byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile); + cryptor.readKeysFromMasterkeyFile(masterkeyFileContents, passphrase); + // create backup, as soon as we know the password was correct: + final Path masterkeyBackupFile = vault.path().getValue().resolve(Constants.MASTERKEY_BACKUP_FILENAME); + Files.copy(masterkeyFile, masterkeyBackupFile, StandardCopyOption.REPLACE_EXISTING); + // do stuff: + upgrade(vault, cryptor); + // write updated masterkey file: + final byte[] upgradedMasterkeyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase); + final Path masterkeyFileAfterUpgrading = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); // path may have changed + Files.write(masterkeyFileAfterUpgrading, upgradedMasterkeyFileContents, StandardOpenOption.TRUNCATE_EXISTING); + } catch (InvalidPassphraseException e) { + throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword")); + } catch (IOException | UnsupportedVaultFormatException e) { + LOG.warn("Upgrade failed.", e); + throw new UpgradeFailedException("Upgrade failed. Details in log message."); + } finally { + cryptor.destroy(); + } + } + + protected abstract void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException; + + /** + * Determines in O(1), if an upgrade can be applied to a vault. + * + * @return true if and only if the vault can be migrated to a newer version without the risk of data losses. + */ + public abstract boolean isApplicable(Vault vault); + + /** + * Thrown when data migration failed. + */ + public class UpgradeFailedException extends Exception { + + UpgradeFailedException() { + } + + UpgradeFailedException(String message) { + super(message); + } + + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java index 36a21b1fb..125646069 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3DropBundleExtension.java @@ -4,19 +4,36 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; + import org.apache.commons.lang3.StringUtils; +import org.cryptomator.crypto.engine.Cryptor; +import org.cryptomator.crypto.engine.InvalidPassphraseException; +import org.cryptomator.crypto.engine.UnsupportedVaultFormatException; +import org.cryptomator.filesystem.crypto.Constants; import org.cryptomator.ui.settings.Localization; +import org.cryptomator.ui.settings.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javafx.application.Platform; -class UpgradeVersion3DropBundleExtension implements UpgradeInstruction { +@Singleton +class UpgradeVersion3DropBundleExtension extends UpgradeStrategy { private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3DropBundleExtension.class); + private final Settings settings; + + @Inject + public UpgradeVersion3DropBundleExtension(Provider cryptorProvider, Localization localization, Settings settings) { + super(cryptorProvider, localization); + this.settings = settings; + } @Override - public String getNotification(Vault vault, Localization localization) { + public String getNotification(Vault vault) { String fmt = localization.getString("upgrade.version3dropBundleExtension.msg"); Path path = vault.path().getValue(); String oldVaultName = path.getFileName().toString(); @@ -25,7 +42,26 @@ class UpgradeVersion3DropBundleExtension implements UpgradeInstruction { } @Override - public void upgrade(Vault vault, Localization localization) throws UpgradeFailedException { + public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException { + final Cryptor cryptor = cryptorProvider.get(); + try { + final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); + final byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile); + cryptor.readKeysFromMasterkeyFile(masterkeyFileContents, passphrase); + upgrade(vault, cryptor); + // don't write new masterkey. this is a special case, as we were stupid and didn't increase the vault version with this upgrade... + } catch (InvalidPassphraseException e) { + throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword")); + } catch (IOException | UnsupportedVaultFormatException e) { + LOG.warn("Upgrade failed.", e); + throw new UpgradeFailedException("Upgrade failed. Details in log message."); + } finally { + cryptor.destroy(); + } + } + + @Override + protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException { Path path = vault.path().getValue(); String oldVaultName = path.getFileName().toString(); String newVaultName = StringUtils.removeEnd(oldVaultName, Vault.VAULT_FILE_EXTENSION); @@ -39,6 +75,7 @@ class UpgradeVersion3DropBundleExtension implements UpgradeInstruction { Files.move(path, path.resolveSibling(newVaultName)); Platform.runLater(() -> { vault.setPath(newPath); + settings.save(); }); } catch (IOException e) { LOG.error("Vault migration failed", e); diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java new file mode 100644 index 000000000..f660ae7d3 --- /dev/null +++ b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java @@ -0,0 +1,55 @@ +package org.cryptomator.ui.model; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.cryptomator.crypto.engine.Cryptor; +import org.cryptomator.filesystem.crypto.Constants; +import org.cryptomator.ui.settings.Localization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Singleton +class UpgradeVersion3to4 extends UpgradeStrategy { + + private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3to4.class); + + @Inject + public UpgradeVersion3to4(Provider cryptorProvider, Localization localization) { + super(cryptorProvider, localization); + } + + @Override + public String getNotification(Vault vault) { + return localization.getString("upgrade.version3to4.msg"); + } + + @Override + protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException { + throw new UpgradeFailedException("not yet implemented"); + } + + @Override + public boolean isApplicable(Vault vault) { + final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME); + try { + if (Files.isRegularFile(masterkeyFile)) { + final String keyContents = new String(Files.readAllBytes(masterkeyFile), StandardCharsets.UTF_8); + return keyContents.contains("\"version\":3") || keyContents.contains("\"version\": 3"); + } else { + LOG.warn("Not a file: {}", masterkeyFile); + return false; + } + } catch (IOException e) { + LOG.warn("Could not determine, whether upgrade is applicable.", e); + return false; + } + } + +} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java index 3cbc4bbc1..d38f9152c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java @@ -16,7 +16,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.text.Normalizer; import java.text.Normalizer.Form; -import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -64,9 +63,9 @@ public class Vault implements CryptoFileSystemDelegate { public static final String VAULT_FILE_EXTENSION = ".cryptomator"; private final ObjectProperty path; - private final DeferredCloser closer; private final ShorteningFileSystemFactory shorteningFileSystemFactory; private final CryptoFileSystemFactory cryptoFileSystemFactory; + private final DeferredCloser closer; private final BooleanProperty unlocked = new SimpleBooleanProperty(); private final ObservableList namesOfResourcesWithInvalidMac = FXThreads.observableListOnMainThread(FXCollections.observableArrayList()); private final Set whitelistedResourcesWithInvalidMac = new HashSet<>(); @@ -82,9 +81,9 @@ public class Vault implements CryptoFileSystemDelegate { */ Vault(Path vaultDirectoryPath, ShorteningFileSystemFactory shorteningFileSystemFactory, CryptoFileSystemFactory cryptoFileSystemFactory, DeferredCloser closer) { this.path = new SimpleObjectProperty(vaultDirectoryPath); - this.closer = closer; this.shorteningFileSystemFactory = shorteningFileSystemFactory; this.cryptoFileSystemFactory = cryptoFileSystemFactory; + this.closer = closer; try { setMountName(name().getValue()); @@ -167,16 +166,6 @@ public class Vault implements CryptoFileSystemDelegate { Optionals.ifPresent(filesystemFrontend.get(), Frontend::unmount); } - public boolean needsUpgrade() { - return availableUpgrade().isPresent(); - } - - public Optional availableUpgrade() { - return Arrays.stream(UpgradeInstruction.AVAILABLE_INSTRUCTIONS).filter(instruction -> { - return instruction.isApplicable(this); - }).findAny(); - } - // ****************************************************************************** // Delegate methods // ********************************************************************************/ diff --git a/main/ui/src/main/resources/fxml/upgrade.fxml b/main/ui/src/main/resources/fxml/upgrade.fxml index b153c4f6a..be353953e 100644 --- a/main/ui/src/main/resources/fxml/upgrade.fxml +++ b/main/ui/src/main/resources/fxml/upgrade.fxml @@ -12,10 +12,13 @@ +