From 82a42ea183d0f6de48f5483b33d2213ba335fa69 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 2 Nov 2020 14:03:39 +0100 Subject: [PATCH] Removed keychain module. Implemented new KeychainManager in various UI controller classes --- .../common}/keychain/KeychainManager.java | 40 ++-- .../common/keychain/KeychainModule.java | 4 +- .../NoKeychainAccessProviderException.java | 13 ++ .../common}/keychain/KeychainManagerTest.java | 11 +- .../common}/keychain/MapKeychainAccess.java | 6 +- main/keychain/pom.xml | 69 ------ .../keychain/KeychainAccessException.java | 12 - .../keychain/KeychainAccessStrategy.java | 46 ---- .../cryptomator/keychain/KeychainModule.java | 45 ---- .../LinuxKDEWalletKeychainAccessImpl.java | 126 ----------- .../LinuxSecretServiceKeychainAccessImpl.java | 81 ------- .../keychain/LinuxSystemKeychainAccess.java | 106 --------- .../keychain/MacSystemKeychainAccess.java | 57 ----- .../WindowsProtectedKeychainAccess.java | 209 ------------------ .../WindowsProtectedKeychainAccessTest.java | 56 ----- main/keychain/src/test/resources/log4j2.xml | 33 --- main/pom.xml | 18 -- main/ui/pom.xml | 4 - .../ChangePasswordController.java | 12 +- .../cryptomator/ui/common/VaultService.java | 6 +- .../ForgetPasswordController.java | 13 +- .../ui/launcher/UiLauncherModule.java | 3 +- .../VaultDetailLockedController.java | 16 +- .../ui/migration/MigrationRunController.java | 16 +- .../GeneralPreferencesController.java | 5 - .../ui/unlock/UnlockController.java | 8 +- .../cryptomator/ui/unlock/UnlockModule.java | 16 +- .../cryptomator/ui/unlock/UnlockWorkflow.java | 12 +- .../MasterkeyOptionsController.java | 36 +-- .../main/resources/license/THIRD-PARTY.txt | 9 +- 30 files changed, 126 insertions(+), 962 deletions(-) rename main/{keychain/src/main/java/org/cryptomator => commons/src/main/java/org/cryptomator/common}/keychain/KeychainManager.java (73%) create mode 100644 main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java rename main/{keychain/src/test/java/org/cryptomator => commons/src/test/java/org/cryptomator/common}/keychain/KeychainManagerTest.java (76%) rename main/{keychain/src/test/java/org/cryptomator => commons/src/test/java/org/cryptomator/common}/keychain/MapKeychainAccess.java (86%) delete mode 100644 main/keychain/pom.xml delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java delete mode 100644 main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java delete mode 100644 main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java delete mode 100644 main/keychain/src/test/resources/log4j2.xml diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainManager.java b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java similarity index 73% rename from main/keychain/src/main/java/org/cryptomator/keychain/KeychainManager.java rename to main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java index 515195232..e91648f7d 100644 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainManager.java +++ b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainManager.java @@ -1,60 +1,75 @@ -package org.cryptomator.keychain; +package org.cryptomator.common.keychain; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import org.cryptomator.integrations.keychain.KeychainAccessException; +import org.cryptomator.integrations.keychain.KeychainAccessProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Singleton; import javafx.application.Platform; +import javafx.beans.binding.ObjectExpression; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import java.util.Arrays; -public class KeychainManager implements KeychainAccessStrategy { +@Singleton +public class KeychainManager implements KeychainAccessProvider { private static final Logger LOG = LoggerFactory.getLogger(KeychainManager.class); - private final KeychainAccessStrategy keychain; + private final ObjectExpression keychain; private LoadingCache passphraseStoredProperties; - KeychainManager(KeychainAccessStrategy keychain) { - assert keychain.isSupported(); - this.keychain = keychain; + @Inject + KeychainManager(ObjectExpression selectedKeychain) { + this.keychain = selectedKeychain; this.passphraseStoredProperties = CacheBuilder.newBuilder() // .weakValues() // .build(CacheLoader.from(this::createStoredPassphraseProperty)); + keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll()); + } + + private KeychainAccessProvider getKeychainOrFail() throws KeychainAccessException { + var result = keychain.getValue(); + if (result == null) { + throw new NoKeychainAccessProviderException(); + } + return result; } @Override public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - keychain.storePassphrase(key, passphrase); + getKeychainOrFail().storePassphrase(key, passphrase); setPassphraseStored(key, true); } @Override public char[] loadPassphrase(String key) throws KeychainAccessException { - char[] passphrase = keychain.loadPassphrase(key); + char[] passphrase = getKeychainOrFail().loadPassphrase(key); setPassphraseStored(key, passphrase != null); return passphrase; } @Override public void deletePassphrase(String key) throws KeychainAccessException { - keychain.deletePassphrase(key); + getKeychainOrFail().deletePassphrase(key); setPassphraseStored(key, false); } @Override public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - keychain.changePassphrase(key, passphrase); + getKeychainOrFail().changePassphrase(key, passphrase); setPassphraseStored(key, true); } @Override public boolean isSupported() { - return true; + return keychain.getValue() != null; } /** @@ -69,7 +84,7 @@ public class KeychainManager implements KeychainAccessStrategy { public boolean isPassphraseStored(String key) throws KeychainAccessException { char[] storedPw = null; try { - storedPw = keychain.loadPassphrase(key); + storedPw = getKeychainOrFail().loadPassphrase(key); return storedPw != null; } finally { if (storedPw != null) { @@ -107,7 +122,6 @@ public class KeychainManager implements KeychainAccessStrategy { private BooleanProperty createStoredPassphraseProperty(String key) { try { - LOG.warn("LOAD"); // TODO remove return new SimpleBooleanProperty(isPassphraseStored(key)); } catch (KeychainAccessException e) { return new SimpleBooleanProperty(false); diff --git a/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java index 895781c8f..4e88fe027 100644 --- a/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java +++ b/main/commons/src/main/java/org/cryptomator/common/keychain/KeychainModule.java @@ -8,6 +8,8 @@ import org.cryptomator.integrations.keychain.KeychainAccessProvider; import javax.inject.Singleton; import javafx.beans.binding.Bindings; import javafx.beans.binding.ObjectBinding; +import javafx.beans.binding.ObjectExpression; +import javafx.beans.value.ObservableValue; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Collectors; @@ -29,7 +31,7 @@ public class KeychainModule { @Provides @Singleton - static ObjectBinding provideKeychainAccessProvider(Settings settings, Set providers) { + static ObjectExpression provideKeychainAccessProvider(Settings settings, Set providers) { return Bindings.createObjectBinding(() -> { var selectedProviderClass = settings.keychainBackend().get().getProviderClass(); var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny(); diff --git a/main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java b/main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java new file mode 100644 index 000000000..c14b81076 --- /dev/null +++ b/main/commons/src/main/java/org/cryptomator/common/keychain/NoKeychainAccessProviderException.java @@ -0,0 +1,13 @@ +package org.cryptomator.common.keychain; + +import org.cryptomator.integrations.keychain.KeychainAccessException; + +/** + * Thrown by {@link KeychainManager} if attempted to access a keychain despite no supported keychain access provider being available. + */ +public class NoKeychainAccessProviderException extends KeychainAccessException { + + public NoKeychainAccessProviderException() { + super("Did not find any supported keychain access provider."); + } +} diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainManagerTest.java b/main/commons/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java similarity index 76% rename from main/keychain/src/test/java/org/cryptomator/keychain/KeychainManagerTest.java rename to main/commons/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java index 8577b0977..e82e67e2d 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainManagerTest.java +++ b/main/commons/src/test/java/org/cryptomator/common/keychain/KeychainManagerTest.java @@ -1,23 +1,26 @@ -package org.cryptomator.keychain; +package org.cryptomator.common.keychain; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import javafx.application.Platform; import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -class KeychainManagerTest { +public class KeychainManagerTest { @Test public void testStoreAndLoad() throws KeychainAccessException { - KeychainManager keychainManager = new KeychainManager(new MapKeychainAccess()); + KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess())); keychainManager.storePassphrase("test", "asd"); Assertions.assertArrayEquals("asd".toCharArray(), keychainManager.loadPassphrase("test")); } @@ -34,7 +37,7 @@ class KeychainManagerTest { @Test public void testPropertyChangesWhenStoringPassword() throws KeychainAccessException, InterruptedException { - KeychainManager keychainManager = new KeychainManager(new MapKeychainAccess()); + KeychainManager keychainManager = new KeychainManager(new SimpleObjectProperty<>(new MapKeychainAccess())); ReadOnlyBooleanProperty property = keychainManager.getPassphraseStoredProperty("test"); Assertions.assertEquals(false, property.get()); diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java b/main/commons/src/test/java/org/cryptomator/common/keychain/MapKeychainAccess.java similarity index 86% rename from main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java rename to main/commons/src/test/java/org/cryptomator/common/keychain/MapKeychainAccess.java index 26b301377..c571ad716 100644 --- a/main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java +++ b/main/commons/src/test/java/org/cryptomator/common/keychain/MapKeychainAccess.java @@ -3,12 +3,14 @@ * All rights reserved. This program and the accompanying materials * are made available under the terms of the accompanying LICENSE file. *******************************************************************************/ -package org.cryptomator.keychain; +package org.cryptomator.common.keychain; + +import org.cryptomator.integrations.keychain.KeychainAccessProvider; import java.util.HashMap; import java.util.Map; -class MapKeychainAccess implements KeychainAccessStrategy { +class MapKeychainAccess implements KeychainAccessProvider { private final Map map = new HashMap<>(); diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml deleted file mode 100644 index c70d7cacb..000000000 --- a/main/keychain/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - 4.0.0 - - org.cryptomator - main - 1.6.0-SNAPSHOT - - keychain - System Keychain Access - - - - org.cryptomator - commons - - - - - org.openjfx - javafx-base - - - org.openjfx - javafx-graphics - - - - - org.apache.commons - commons-lang3 - - - - - com.google.code.gson - gson - - - com.google.guava - guava - - - - - com.google.dagger - dagger - - - - - de.swiesend - secret-service - - - - - org.purejava - kdewallet - - - - - org.slf4j - slf4j-simple - test - - - \ No newline at end of file diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java deleted file mode 100644 index b4f0cf5c8..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessException.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.cryptomator.keychain; - -/** - * Indicates an error during communication with the operating system's keychain. - */ -public class KeychainAccessException extends Exception { - - KeychainAccessException(Throwable cause) { - super(cause); - } - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java deleted file mode 100644 index 553ce4eab..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -public interface KeychainAccessStrategy { - - /** - * Associates a passphrase with a given key. - * - * @param key Key used to retrieve the passphrase via {@link #loadPassphrase(String)}. - * @param passphrase The secret to store in this keychain. - */ - void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException; - - /** - * @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}. - * @return The stored passphrase for the given key or null if no value for the given key could be found. - */ - char[] loadPassphrase(String key) throws KeychainAccessException; - - /** - * Deletes a passphrase with a given key. - * - * @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}. - */ - void deletePassphrase(String key) throws KeychainAccessException; - - /** - * Updates a passphrase with a given key. Noop, if there is no item for the given key. - * - * @param key Unique key previously used while {@link #storePassphrase(String, CharSequence) storing a passphrase}. - * @param passphrase The secret to be updated in this keychain. - */ - void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException; - - /** - * @return true if this KeychainAccessStrategy works on the current machine. - * @implNote This method must not throw any exceptions and should fail fast - * returning false if it can't determine availability of the checked strategy - */ - boolean isSupported(); - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java deleted file mode 100644 index 791eb1dd0..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import dagger.Binds; -import dagger.Module; -import dagger.Provides; -import dagger.multibindings.IntoSet; -import org.cryptomator.common.JniModule; - -import javax.inject.Singleton; -import java.util.Optional; -import java.util.Set; - -@Module(includes = {JniModule.class}) -public abstract class KeychainModule { - - @Binds - @IntoSet - abstract KeychainAccessStrategy bindMacSystemKeychainAccess(MacSystemKeychainAccess keychainAccessStrategy); - - @Binds - @IntoSet - abstract KeychainAccessStrategy bindWindowsProtectedKeychainAccess(WindowsProtectedKeychainAccess keychainAccessStrategy); - - @Binds - @IntoSet - abstract KeychainAccessStrategy bindLinuxSystemKeychainAccess(LinuxSystemKeychainAccess keychainAccessStrategy); - - @Provides - @Singleton - static Optional provideSupportedKeychain(Set keychainAccessStrategies) { - return keychainAccessStrategies.stream().filter(KeychainAccessStrategy::isSupported).findFirst(); - } - - @Provides - @Singleton - public static Optional provideKeychainManager(Optional keychainAccess) { - return keychainAccess.map(KeychainManager::new); - } - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java deleted file mode 100644 index 1f74bd18d..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxKDEWalletKeychainAccessImpl.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.cryptomator.keychain; - -import org.freedesktop.dbus.connections.impl.DBusConnection; -import org.freedesktop.dbus.exceptions.DBusException; -import org.kde.KWallet; -import org.kde.Static; -import org.purejava.KDEWallet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class LinuxKDEWalletKeychainAccessImpl implements KeychainAccessStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(LinuxKDEWalletKeychainAccessImpl.class); - - private final String FOLDER_NAME = "Cryptomator"; - private final String APP_NAME = "Cryptomator"; - private DBusConnection connection; - private KDEWallet wallet; - private int handle = -1; - - public LinuxKDEWalletKeychainAccessImpl() throws KeychainAccessException { - try { - connection = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION); - } catch (DBusException e) { - LOG.error("Connecting to D-Bus failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public boolean isSupported() { - try { - wallet = new KDEWallet(connection); - return wallet.isEnabled(); - } catch (Exception e) { - LOG.error("A KDEWallet could not be created:", e); - return false; - } - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try { - if (walletIsOpen() && // - !(wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) // - && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1) // - && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) { - LOG.debug("Passphrase successfully stored."); - } else { - LOG.debug("Passphrase was not stored."); - } - } catch (Exception e) { - LOG.error("Storing the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public char[] loadPassphrase(String key) throws KeychainAccessException { - String password = ""; - try { - if (walletIsOpen()) { - password = wallet.readPassword(handle, FOLDER_NAME, key, APP_NAME); - LOG.debug("loadPassphrase: wallet is open."); - } else { - LOG.debug("loadPassphrase: wallet is closed."); - } - return (password.equals("")) ? null : password.toCharArray(); - } catch (Exception e) { - LOG.error("Loading the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public void deletePassphrase(String key) throws KeychainAccessException { - try { - if (walletIsOpen() // - && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) // - && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1 // - && wallet.removeEntry(handle, FOLDER_NAME, key, APP_NAME) == 0) { - LOG.debug("Passphrase successfully deleted."); - } else { - LOG.debug("Passphrase was not deleted."); - } - } catch (Exception e) { - LOG.error("Deleting the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try { - if (walletIsOpen() // - && wallet.hasEntry(handle, FOLDER_NAME, key, APP_NAME) // - && wallet.entryType(handle, FOLDER_NAME, key, APP_NAME) == 1 // - && wallet.writePassword(handle, FOLDER_NAME, key, passphrase.toString(), APP_NAME) == 0) { - LOG.debug("Passphrase successfully changed."); - } else { - LOG.debug("Passphrase could not be changed."); - } - } catch (Exception e) { - LOG.error("Changing the passphrase failed:", e); - throw new KeychainAccessException(e); - } - } - - private boolean walletIsOpen() throws KeychainAccessException { - try { - if (wallet.isOpen(Static.DEFAULT_WALLET)) { - // This is needed due to KeechainManager loading the passphase directly - if (handle == -1) handle = wallet.open(Static.DEFAULT_WALLET, 0, APP_NAME); - return true; - } - wallet.openAsync(Static.DEFAULT_WALLET, 0, APP_NAME, false); - wallet.getSignalHandler().await(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5, () -> null); - handle = wallet.getSignalHandler().getLastHandledSignal(KWallet.walletAsyncOpened.class, Static.ObjectPaths.KWALLETD5).handle; - LOG.debug("Wallet successfully initialized."); - return handle != -1; - } catch (Exception e) { - LOG.error("Asynchronous opening the wallet failed:", e); - throw new KeychainAccessException(e); - } - } -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java deleted file mode 100644 index e422fff2e..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSecretServiceKeychainAccessImpl.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.cryptomator.keychain; - -import org.freedesktop.secret.simple.SimpleCollection; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -class LinuxSecretServiceKeychainAccessImpl implements KeychainAccessStrategy { - - private final String LABEL_FOR_SECRET_IN_KEYRING = "Cryptomator"; - - @Override - public boolean isSupported() { - try (@SuppressWarnings("unused") SimpleCollection keyring = new SimpleCollection()) { - // seems like we're able to access the keyring. - return true; - } catch (IOException | RuntimeException e) { - return false; - } - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list == null) { - keyring.createItem(LABEL_FOR_SECRET_IN_KEYRING, passphrase, createAttributes(key)); - } else { - changePassphrase(key, passphrase); - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - @Override - public char[] loadPassphrase(String key) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list != null) { - return keyring.getSecret(list.get(0)); - } else { - return null; - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - @Override - public void deletePassphrase(String key) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list != null) { - keyring.deleteItem(list.get(0)); - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - try (SimpleCollection keyring = new SimpleCollection()) { - List list = keyring.getItems(createAttributes(key)); - if (list != null) { - keyring.updateItem(list.get(0), LABEL_FOR_SECRET_IN_KEYRING, passphrase, createAttributes(key)); - } - } catch (IOException e) { - throw new KeychainAccessException(e); - } - } - - private Map createAttributes(String key) { - Map attributes = new HashMap(); - attributes.put("Vault", key); - return attributes; - } -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java deleted file mode 100644 index f49ea5d1d..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/LinuxSystemKeychainAccess.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.cryptomator.keychain; - -import javafx.beans.property.ObjectProperty; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.settings.KeychainBackend; -import org.cryptomator.common.settings.Settings; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.EnumSet; -import java.util.Optional; - -/** - * A facade to LinuxSecretServiceKeychainAccessImpl and LinuxKDEWalletKeychainAccessImpl - * that depend on libraries that are unavailable on Mac and Windows. - */ -@Singleton -public class LinuxSystemKeychainAccess implements KeychainAccessStrategy { - - // the actual implementation is hidden in this delegate objects which are loaded via reflection, - // as it depends on libraries that aren't necessarily available: - private final Optional delegate; - private final Settings settings; - private static EnumSet availableKeychainBackends = EnumSet.noneOf(KeychainBackend.class); - private static KeychainBackend backendActivated = null; - private static boolean isGnomeKeyringAvailable; - private static boolean isKdeWalletAvailable; - - @Inject - public LinuxSystemKeychainAccess(Settings settings) { - this.settings = settings; - this.delegate = constructKeychainAccess(); - } - - private Optional constructKeychainAccess() { - try { // find out which backends are available - Class clazz = Class.forName("org.cryptomator.keychain.LinuxSecretServiceKeychainAccessImpl"); - KeychainAccessStrategy gnomeKeyring = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance(); - if (gnomeKeyring.isSupported()) { - LinuxSystemKeychainAccess.availableKeychainBackends.add(KeychainBackend.GNOME); - LinuxSystemKeychainAccess.isGnomeKeyringAvailable = true; - } - clazz = Class.forName("org.cryptomator.keychain.LinuxKDEWalletKeychainAccessImpl"); - KeychainAccessStrategy kdeWallet = (KeychainAccessStrategy) clazz.getDeclaredConstructor().newInstance(); - if (kdeWallet.isSupported()) { - LinuxSystemKeychainAccess.availableKeychainBackends.add(KeychainBackend.KDE); - LinuxSystemKeychainAccess.isKdeWalletAvailable = true; - } - - // load password backend setting as the preferred backend - ObjectProperty pwSetting = settings.keychainBackend(); - - // check for GNOME keyring first, as this gets precedence over - // KDE wallet as the former was implemented first - if (isGnomeKeyringAvailable && pwSetting.get().equals(KeychainBackend.GNOME)) { - pwSetting.setValue(KeychainBackend.GNOME); - LinuxSystemKeychainAccess.backendActivated = KeychainBackend.GNOME; - return Optional.of(gnomeKeyring); - } - - if (isKdeWalletAvailable && pwSetting.get().equals(KeychainBackend.KDE)) { - pwSetting.setValue(KeychainBackend.KDE); - LinuxSystemKeychainAccess.backendActivated = KeychainBackend.KDE; - return Optional.of(kdeWallet); - } - return Optional.empty(); - } catch (Exception e) { - return Optional.empty(); - } - } - - /* Getter/Setter */ - - public static EnumSet getAvailableKeychainBackends() { - return availableKeychainBackends; - } - - public static KeychainBackend getBackendActivated() { - return backendActivated; - } - - @Override - public boolean isSupported() { - return SystemUtils.IS_OS_LINUX && delegate.map(KeychainAccessStrategy::isSupported).orElse(false); - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - delegate.orElseThrow(IllegalStateException::new).storePassphrase(key, passphrase); - } - - @Override - public char[] loadPassphrase(String key) throws KeychainAccessException { - return delegate.orElseThrow(IllegalStateException::new).loadPassphrase(key); - } - - @Override - public void deletePassphrase(String key) throws KeychainAccessException { - delegate.orElseThrow(IllegalStateException::new).deletePassphrase(key); - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException { - delegate.orElseThrow(IllegalStateException::new).changePassphrase(key, passphrase); - } -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java deleted file mode 100644 index 39a6cd756..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.jni.MacFunctions; -import org.cryptomator.jni.MacKeychainAccess; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Optional; - -@Singleton -class MacSystemKeychainAccess implements KeychainAccessStrategy { - - private final Optional macFunctions; - - @Inject - public MacSystemKeychainAccess(Optional macFunctions) { - this.macFunctions = macFunctions; - } - - private MacKeychainAccess keychain() { - return macFunctions.orElseThrow(IllegalStateException::new).keychainAccess(); - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) { - keychain().storePassword(key, passphrase); - } - - @Override - public char[] loadPassphrase(String key) { - return keychain().loadPassword(key); - } - - @Override - public boolean isSupported() { - return SystemUtils.IS_OS_MAC_OSX && macFunctions.isPresent(); - } - - @Override - public void deletePassphrase(String key) { - keychain().deletePassword(key); - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) { - if (keychain().deletePassword(key)) { - keychain().storePassword(key, passphrase); - } - } - -} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java deleted file mode 100644 index 6668104a4..000000000 --- a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsProtectedKeychainAccess.java +++ /dev/null @@ -1,209 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import com.google.common.io.BaseEncoding; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.Environment; -import org.cryptomator.jni.WinDataProtection; -import org.cryptomator.jni.WinFunctions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.UncheckedIOException; -import java.io.Writer; -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; - -import static java.nio.charset.StandardCharsets.UTF_8; - -@Singleton -class WindowsProtectedKeychainAccess implements KeychainAccessStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(WindowsProtectedKeychainAccess.class); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting() // - .registerTypeHierarchyAdapter(byte[].class, new ByteArrayJsonAdapter()) // - .disableHtmlEscaping().create(); - - private final Optional winFunctions; - private final List keychainPaths; - private Map keychainEntries; - - @Inject - public WindowsProtectedKeychainAccess(Optional winFunctions, Environment environment) { - this.winFunctions = winFunctions; - this.keychainPaths = environment.getKeychainPath().collect(Collectors.toList()); - } - - private WinDataProtection dataProtection() { - return winFunctions.orElseThrow(IllegalStateException::new).dataProtection(); - } - - @Override - public void storePassphrase(String key, CharSequence passphrase) { - loadKeychainEntriesIfNeeded(); - ByteBuffer buf = UTF_8.encode(CharBuffer.wrap(passphrase)); - byte[] cleartext = new byte[buf.remaining()]; - buf.get(cleartext); - KeychainEntry entry = new KeychainEntry(); - entry.salt = generateSalt(); - entry.ciphertext = dataProtection().protect(cleartext, entry.salt); - Arrays.fill(buf.array(), (byte) 0x00); - Arrays.fill(cleartext, (byte) 0x00); - keychainEntries.put(key, entry); - saveKeychainEntries(); - } - - @Override - public char[] loadPassphrase(String key) { - loadKeychainEntriesIfNeeded(); - KeychainEntry entry = keychainEntries.get(key); - if (entry == null) { - return null; - } - byte[] cleartext = dataProtection().unprotect(entry.ciphertext, entry.salt); - if (cleartext == null) { - return null; - } - CharBuffer buf = UTF_8.decode(ByteBuffer.wrap(cleartext)); - char[] passphrase = new char[buf.remaining()]; - buf.get(passphrase); - Arrays.fill(cleartext, (byte) 0x00); - Arrays.fill(buf.array(), (char) 0x00); - return passphrase; - } - - @Override - public void deletePassphrase(String key) { - loadKeychainEntriesIfNeeded(); - keychainEntries.remove(key); - saveKeychainEntries(); - } - - @Override - public void changePassphrase(String key, CharSequence passphrase) { - loadKeychainEntriesIfNeeded(); - if (keychainEntries.remove(key) != null) { - storePassphrase(key, passphrase); - } - } - - @Override - public boolean isSupported() { - return SystemUtils.IS_OS_WINDOWS && winFunctions.isPresent() && !keychainPaths.isEmpty(); - } - - private byte[] generateSalt() { - byte[] result = new byte[2 * Long.BYTES]; - UUID uuid = UUID.randomUUID(); - ByteBuffer buf = ByteBuffer.wrap(result); - buf.putLong(uuid.getMostSignificantBits()); - buf.putLong(uuid.getLeastSignificantBits()); - return result; - } - - private void loadKeychainEntriesIfNeeded() { - if (keychainEntries == null) { - for (Path keychainPath : keychainPaths) { - Optional> keychain = loadKeychainEntries(keychainPath); - if (keychain.isPresent()) { - keychainEntries = keychain.get(); - break; - } - } - } - if (keychainEntries == null) { - LOG.info("Unable to load existing keychain file, creating new keychain."); - keychainEntries = new HashMap<>(); - } - } - - private Optional> loadKeychainEntries(Path keychainPath) { - LOG.debug("Attempting to load keychain from {}", keychainPath); - Type type = new TypeToken>() { - }.getType(); - try (InputStream in = Files.newInputStream(keychainPath, StandardOpenOption.READ); // - Reader reader = new InputStreamReader(in, UTF_8)) { - return Optional.of(GSON.fromJson(reader, type)); - } catch (NoSuchFileException | JsonParseException e) { - return Optional.empty(); - } catch (IOException e) { - throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e); - } - } - - private void saveKeychainEntries() { - if (keychainPaths.isEmpty()) { - throw new IllegalStateException("Can't save keychain if no keychain path is specified."); - } - saveKeychainEntries(keychainPaths.get(0)); - } - - private void saveKeychainEntries(Path keychainPath) { - try (OutputStream out = Files.newOutputStream(keychainPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); // - Writer writer = new OutputStreamWriter(out, UTF_8)) { - GSON.toJson(keychainEntries, writer); - } catch (IOException e) { - throw new UncheckedIOException("Could not read keychain from path " + keychainPath, e); - } - } - - private static class KeychainEntry { - - @SerializedName("ciphertext") - byte[] ciphertext; - @SerializedName("salt") - byte[] salt; - } - - private static class ByteArrayJsonAdapter implements JsonSerializer, JsonDeserializer { - - private static final BaseEncoding BASE64 = BaseEncoding.base64(); - - @Override - public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - return BASE64.decode(json.getAsString()); - } - - @Override - public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(BASE64.encode(src)); - } - - } - -} diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java b/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java deleted file mode 100644 index aa5c2b2f4..000000000 --- a/main/keychain/src/test/java/org/cryptomator/keychain/WindowsProtectedKeychainAccessTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.keychain; - -import org.cryptomator.common.Environment; -import org.cryptomator.jni.WinDataProtection; -import org.cryptomator.jni.WinFunctions; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.mockito.Mockito; -import org.mockito.stubbing.Answer; - -import java.nio.file.Path; -import java.util.Optional; -import java.util.stream.Stream; - -public class WindowsProtectedKeychainAccessTest { - - private WindowsProtectedKeychainAccess keychain; - - @BeforeEach - public void setup(@TempDir Path tempDir) { - Path keychainPath = tempDir.resolve("keychainfile.tmp"); - Environment env = Mockito.mock(Environment.class); - Mockito.when(env.getKeychainPath()).thenReturn(Stream.of(keychainPath)); - WinFunctions winFunctions = Mockito.mock(WinFunctions.class); - WinDataProtection winDataProtection = Mockito.mock(WinDataProtection.class); - Mockito.when(winFunctions.dataProtection()).thenReturn(winDataProtection); - Answer answerReturningFirstArg = invocation -> ((byte[]) invocation.getArgument(0)).clone(); - Mockito.when(winDataProtection.protect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg); - Mockito.when(winDataProtection.unprotect(Mockito.any(), Mockito.any())).thenAnswer(answerReturningFirstArg); - keychain = new WindowsProtectedKeychainAccess(Optional.of(winFunctions), env); - } - - @Test - public void testStoreAndLoad() { - String storedPw1 = "topSecret"; - String storedPw2 = "bottomSecret"; - keychain.storePassphrase("myPassword", storedPw1); - keychain.storePassphrase("myOtherPassword", storedPw2); - String loadedPw1 = new String(keychain.loadPassphrase("myPassword")); - String loadedPw2 = new String(keychain.loadPassphrase("myOtherPassword")); - Assertions.assertEquals(storedPw1, loadedPw1); - Assertions.assertEquals(storedPw2, loadedPw2); - keychain.deletePassphrase("myPassword"); - Assertions.assertNull(keychain.loadPassphrase("myPassword")); - Assertions.assertNotNull(keychain.loadPassphrase("myOtherPassword")); - Assertions.assertNull(keychain.loadPassphrase("nonExistingPassword")); - } - -} diff --git a/main/keychain/src/test/resources/log4j2.xml b/main/keychain/src/test/resources/log4j2.xml deleted file mode 100644 index 3e9a7bd67..000000000 --- a/main/keychain/src/test/resources/log4j2.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/main/pom.xml b/main/pom.xml index c4bd8dcc4..625e2eb49 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -68,11 +68,6 @@ commons ${project.version} - - org.cryptomator - keychain - ${project.version} - org.cryptomator ui @@ -187,18 +182,6 @@ ${commons-lang3.version} - - - de.swiesend - secret-service - ${secret-service.version} - - - org.purejava - kdewallet - ${kdewallet.version} - - com.auth0 @@ -280,7 +263,6 @@ commons - keychain ui launcher diff --git a/main/ui/pom.xml b/main/ui/pom.xml index 79b59eeec..7784ee802 100644 --- a/main/ui/pom.xml +++ b/main/ui/pom.xml @@ -10,10 +10,6 @@ Cryptomator GUI - - org.cryptomator - keychain - org.cryptomator commons diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java index e1fc9c093..52cfe2b81 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java @@ -1,10 +1,10 @@ package org.cryptomator.ui.changepassword; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; @@ -36,14 +36,14 @@ public class ChangePasswordController implements FxController { private final Vault vault; private final ObjectProperty newPassword; private final ErrorComponent.Builder errorComponent; - private final Optional keychain; + private final KeychainManager keychain; public NiceSecurePasswordField oldPasswordField; public CheckBox finalConfirmationCheckbox; public Button finishButton; @Inject - public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty newPassword, ErrorComponent.Builder errorComponent, Optional keychain) { + public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, @Named("newPassword") ObjectProperty newPassword, ErrorComponent.Builder errorComponent, KeychainManager keychain) { this.window = window; this.vault = vault; this.newPassword = newPassword; @@ -82,9 +82,9 @@ public class ChangePasswordController implements FxController { } private void updatePasswordInSystemkeychain() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { try { - keychain.get().changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get())); + keychain.changePassphrase(vault.getId(), CharBuffer.wrap(newPassword.get())); LOG.info("Successfully updated password in system keychain for {}", vault.getDisplayName()); } catch (KeychainAccessException e) { LOG.error("Failed to update password in system keychain.", e); diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java index 2631c0f6a..35ed49870 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java @@ -3,7 +3,6 @@ package org.cryptomator.ui.common; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.Volume; -import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.fxapp.FxApplicationScoped; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,7 +13,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; @@ -25,12 +23,10 @@ public class VaultService { private static final Logger LOG = LoggerFactory.getLogger(VaultService.class); private final ExecutorService executorService; - private final Optional keychain; @Inject - public VaultService(ExecutorService executorService, Optional keychain) { + public VaultService(ExecutorService executorService) { this.executorService = executorService; - this.keychain = keychain; } public void reveal(Vault vault) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java index 99f8cbc41..81d28f682 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/forgetPassword/ForgetPasswordController.java @@ -1,8 +1,8 @@ package org.cryptomator.ui.forgetPassword; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.FxController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,7 +11,6 @@ import javax.inject.Inject; import javafx.beans.property.BooleanProperty; import javafx.fxml.FXML; import javafx.stage.Stage; -import java.util.Optional; @ForgetPasswordScoped public class ForgetPasswordController implements FxController { @@ -20,11 +19,11 @@ public class ForgetPasswordController implements FxController { private final Stage window; private final Vault vault; - private final Optional keychain; + private final KeychainManager keychain; private final BooleanProperty confirmedResult; @Inject - public ForgetPasswordController(@ForgetPasswordWindow Stage window, @ForgetPasswordWindow Vault vault, Optional keychain, @ForgetPasswordWindow BooleanProperty confirmedResult) { + public ForgetPasswordController(@ForgetPasswordWindow Stage window, @ForgetPasswordWindow Vault vault, KeychainManager keychain, @ForgetPasswordWindow BooleanProperty confirmedResult) { this.window = window; this.vault = vault; this.keychain = keychain; @@ -38,9 +37,9 @@ public class ForgetPasswordController implements FxController { @FXML public void finish() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { try { - keychain.get().deletePassphrase(vault.getId()); + keychain.deletePassphrase(vault.getId()); LOG.debug("Forgot password for vault {}.", vault.getDisplayName()); confirmedResult.setValue(true); } catch (KeychainAccessException e) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java index 9b8d6e776..2ac585763 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java @@ -3,7 +3,6 @@ package org.cryptomator.ui.launcher; import dagger.Module; import dagger.Provides; import org.cryptomator.common.JniModule; -import org.cryptomator.keychain.KeychainModule; import org.cryptomator.ui.fxapp.FxApplicationComponent; import org.cryptomator.ui.traymenu.TrayMenuComponent; @@ -13,7 +12,7 @@ import java.util.ResourceBundle; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -@Module(includes = {JniModule.class, KeychainModule.class}, subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class}) +@Module(includes = {JniModule.class}, subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class}) public abstract class UiLauncherModule { @Provides diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java index 367aacc3a..e7da43938 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailLockedController.java @@ -1,8 +1,8 @@ package org.cryptomator.ui.mainwindow; import com.tobiasdiez.easybind.EasyBind; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.fxapp.FxApplication; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; @@ -22,19 +22,19 @@ public class VaultDetailLockedController implements FxController { private final ReadOnlyObjectProperty vault; private final FxApplication application; private final VaultOptionsComponent.Builder vaultOptionsWindow; - private final Optional keychainManagerOptional; + private final KeychainManager keychain; private final Stage mainWindow; private final BooleanExpression passwordSaved; @Inject - VaultDetailLockedController(ObjectProperty vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, Optional keychainManagerOptional, @MainWindow Stage mainWindow) { + VaultDetailLockedController(ObjectProperty vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) { this.vault = vault; this.application = application; this.vaultOptionsWindow = vaultOptionsWindow; - this.keychainManagerOptional = keychainManagerOptional; + this.keychain = keychain; this.mainWindow = mainWindow; - if (keychainManagerOptional.isPresent()) { - this.passwordSaved = BooleanExpression.booleanExpression(EasyBind.select(vault).selectObject(v -> keychainManagerOptional.get().getPassphraseStoredProperty(v.getId()))); + if (keychain.isSupported()) { + this.passwordSaved = BooleanExpression.booleanExpression(EasyBind.select(vault).selectObject(v -> keychain.getPassphraseStoredProperty(v.getId()))); } else { this.passwordSaved = new SimpleBooleanProperty(false); } @@ -65,8 +65,8 @@ public class VaultDetailLockedController implements FxController { } public boolean isPasswordSaved() { - if (keychainManagerOptional.isPresent() && vault.get() != null) { - return keychainManagerOptional.get().getPassphraseStoredProperty(vault.get().getId()).get(); + if (keychain.isSupported() && vault.get() != null) { + return keychain.getPassphraseStoredProperty(vault.get().getId()).get(); } else return false; } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java index 2bf4f49a1..830e15819 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.migration; import dagger.Lazy; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.cryptofs.FileNameTooLongException; @@ -9,8 +10,7 @@ import org.cryptomator.cryptofs.migration.Migrators; import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener; import org.cryptomator.cryptofs.migration.api.MigrationProgressListener; import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxController; @@ -37,7 +37,6 @@ import javafx.scene.Scene; import javafx.scene.control.ContentDisplay; import javafx.stage.Stage; import java.util.Arrays; -import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -55,7 +54,7 @@ public class MigrationRunController implements FxController { private final Vault vault; private final ExecutorService executor; private final ScheduledExecutorService scheduler; - private final Optional keychain; + private final KeychainManager keychain; private final ObjectProperty missingCapability; private final ErrorComponent.Builder errorComponent; private final Lazy startScene; @@ -69,8 +68,7 @@ public class MigrationRunController implements FxController { public NiceSecurePasswordField passwordField; @Inject - public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, Optional keychain, @Named("capabilityErrorCause") ObjectProperty missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy impossibleScene, ErrorComponent.Builder errorComponent) { - + public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, KeychainManager keychain, @Named("capabilityErrorCause") ObjectProperty missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy impossibleScene, ErrorComponent.Builder errorComponent) { this.window = window; this.vault = vault; this.executor = executor; @@ -88,7 +86,7 @@ public class MigrationRunController implements FxController { } public void initialize() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { loadStoredPassword(); } migrationButtonDisabled.bind(vault.stateProperty().isNotEqualTo(VaultState.NEEDS_MIGRATION).or(passwordField.textProperty().isEmpty())); @@ -167,10 +165,10 @@ public class MigrationRunController implements FxController { } private void loadStoredPassword() { - assert keychain.isPresent(); + assert keychain.isSupported(); char[] storedPw = null; try { - storedPw = keychain.get().loadPassphrase(vault.getId()); + storedPw = keychain.loadPassphrase(vault.getId()); if (storedPw != null) { passwordField.setPassword(storedPw); passwordField.selectRange(storedPw.length, storedPw.length); diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java index 0d58323ce..208605f51 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java @@ -1,14 +1,11 @@ package org.cryptomator.ui.preferences; -import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.Environment; import org.cryptomator.common.LicenseHolder; import org.cryptomator.common.settings.KeychainBackend; import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.UiTheme; import org.cryptomator.integrations.keychain.KeychainAccessProvider; -import org.cryptomator.keychain.KeychainAccessStrategy; -import org.cryptomator.keychain.LinuxSystemKeychainAccess; import org.cryptomator.ui.common.FxController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,9 +25,7 @@ import javafx.scene.control.RadioButton; import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.util.StringConverter; - import java.util.Arrays; -import java.util.EnumSet; import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index b64195d9a..5460cc149 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -1,7 +1,7 @@ package org.cryptomator.ui.unlock; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.common.WeakBindings; @@ -49,7 +49,7 @@ public class UnlockController implements FxController { private final Optional savedPassword; private final UserInteractionLock passwordEntryLock; private final ForgetPasswordComponent.Builder forgetPassword; - private final Optional keychain; + private final KeychainManager keychain; private final ObjectBinding unlockButtonContentDisplay; private final BooleanBinding userInteractionDisabled; private final BooleanProperty unlockButtonDisabled; @@ -65,7 +65,7 @@ public class UnlockController implements FxController { public Animation unlockAnimation; @Inject - public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, Optional keychain) { + public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, KeychainManager keychain) { this.window = window; this.vault = vault; this.password = password; @@ -214,6 +214,6 @@ public class UnlockController implements FxController { } public boolean isKeychainAccessAvailable() { - return keychain.isPresent(); + return keychain.isSupported(); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java index 102743e7f..1170569c7 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockModule.java @@ -4,9 +4,9 @@ import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.FXMLLoaderFactory; import org.cryptomator.ui.common.FxController; @@ -49,15 +49,17 @@ abstract class UnlockModule { @Provides @Named("savedPassword") @UnlockScoped - static Optional provideStoredPassword(Optional keychain, @UnlockWindow Vault vault) { - return keychain.map(k -> { + static Optional provideStoredPassword(KeychainManager keychain, @UnlockWindow Vault vault) { + if (keychain.isSupported()) { + return Optional.empty(); + } else { try { - return k.loadPassphrase(vault.getId()); + return Optional.ofNullable(keychain.loadPassphrase(vault.getId())); } catch (KeychainAccessException e) { LOG.error("Failed to load entry from system keychain.", e); - return null; + return Optional.empty(); } - }); + } } @Provides diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java index bdefb4ad3..9b95103ad 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java @@ -1,14 +1,14 @@ package org.cryptomator.ui.unlock; import dagger.Lazy; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.mountpoint.InvalidMountPointException; import org.cryptomator.common.vaults.MountPointRequirement; import org.cryptomator.common.vaults.Vault; import org.cryptomator.common.vaults.VaultState; import org.cryptomator.common.vaults.Volume.VolumeException; import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.common.Animations; import org.cryptomator.ui.common.ErrorComponent; import org.cryptomator.ui.common.FxmlFile; @@ -53,14 +53,14 @@ public class UnlockWorkflow extends Task { private final AtomicBoolean savePassword; private final Optional savedPassword; private final UserInteractionLock passwordEntryLock; - private final Optional keychain; + private final KeychainManager keychain; private final Lazy unlockScene; private final Lazy successScene; private final Lazy invalidMountPointScene; private final ErrorComponent.Builder errorComponent; @Inject - UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, Optional keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, ErrorComponent.Builder errorComponent) { + UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, KeychainManager keychain, @FxmlScene(FxmlFile.UNLOCK) Lazy unlockScene, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy invalidMountPointScene, ErrorComponent.Builder errorComponent) { this.window = window; this.vault = vault; this.vaultService = vaultService; @@ -150,9 +150,9 @@ public class UnlockWorkflow extends Task { } private void savePasswordToSystemkeychain() { - if (keychain.isPresent()) { + if (keychain.isSupported()) { try { - keychain.get().storePassphrase(vault.getId(), CharBuffer.wrap(password.get())); + keychain.storePassphrase(vault.getId(), CharBuffer.wrap(password.get())); } catch (KeychainAccessException e) { LOG.error("Failed to store passphrase in system keychain.", e); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java index 4c08ad75a..ee38e1f18 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultoptions/MasterkeyOptionsController.java @@ -1,11 +1,13 @@ package org.cryptomator.ui.vaultoptions; +import org.cryptomator.common.keychain.KeychainManager; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.keychain.KeychainManager; +import org.cryptomator.integrations.keychain.KeychainAccessException; import org.cryptomator.ui.changepassword.ChangePasswordComponent; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.recoverykey.RecoveryKeyComponent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; import javafx.beans.binding.Bindings; @@ -13,29 +15,32 @@ import javafx.beans.binding.BooleanExpression; import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; import javafx.stage.Stage; -import java.util.Optional; @VaultOptionsScoped public class MasterkeyOptionsController implements FxController { + private static final Logger LOG = LoggerFactory.getLogger(MasterkeyOptionsController.class); + private final Vault vault; private final Stage window; private final ChangePasswordComponent.Builder changePasswordWindow; private final RecoveryKeyComponent.Builder recoveryKeyWindow; - private final Optional keychainManagerOptional; + private final KeychainManager keychain; private final BooleanExpression passwordSaved; @Inject - MasterkeyOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow, Optional keychainManagerOptional) { + MasterkeyOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow, KeychainManager keychain) { this.vault = vault; this.window = window; this.changePasswordWindow = changePasswordWindow; this.recoveryKeyWindow = recoveryKeyWindow; - this.keychainManagerOptional = keychainManagerOptional; - if (keychainManagerOptional.isPresent()) { - this.passwordSaved = Bindings.createBooleanBinding(this::isPasswordSaved, keychainManagerOptional.get().getPassphraseStoredProperty(vault.getId())); - } else this.passwordSaved = new SimpleBooleanProperty(false); + this.keychain = keychain; + if (keychain.isSupported()) { + this.passwordSaved = Bindings.createBooleanBinding(this::isPasswordSaved, keychain.getPassphraseStoredProperty(vault.getId())); + } else { + this.passwordSaved = new SimpleBooleanProperty(false); + } } @FXML @@ -54,8 +59,13 @@ public class MasterkeyOptionsController implements FxController { } @FXML - public void removePasswordFromKeychain() throws KeychainAccessException { - keychainManagerOptional.get().deletePassphrase(vault.getId()); + public void removePasswordFromKeychain() { + assert keychain.isSupported(); + try { + keychain.deletePassphrase(vault.getId()); + } catch (KeychainAccessException e) { + LOG.error("Failed to delete passphrase from system keychain.", e); + } window.close(); } @@ -64,8 +74,8 @@ public class MasterkeyOptionsController implements FxController { } public boolean isPasswordSaved() { - if (keychainManagerOptional.isPresent() && vault != null) { - return keychainManagerOptional.get().getPassphraseStoredProperty(vault.getId()).get(); + if (keychain.isSupported() && vault != null) { + return keychain.getPassphraseStoredProperty(vault.getId()).get(); } else return false; } } diff --git a/main/ui/src/main/resources/license/THIRD-PARTY.txt b/main/ui/src/main/resources/license/THIRD-PARTY.txt index 65d2cdb2c..1389d25a1 100644 --- a/main/ui/src/main/resources/license/THIRD-PARTY.txt +++ b/main/ui/src/main/resources/license/THIRD-PARTY.txt @@ -11,15 +11,12 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. -Cryptomator uses 53 third-party dependencies under the following licenses: +Cryptomator uses 46 third-party dependencies under the following licenses: Apache License v2.0: - - HKDF-RFC5869 (at.favre.lib:hkdf:1.1.0 - https://github.com/patrickfav/hkdf) - jffi (com.github.jnr:jffi:1.2.23 - http://github.com/jnr/jffi) - jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm) - jnr-constants (com.github.jnr:jnr-constants:0.9.15 - http://github.com/jnr/jnr-constants) - - jnr-enxio (com.github.jnr:jnr-enxio:0.28 - http://github.com/jnr/jnr-enxio) - jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - http://github.com/jnr/jnr-ffi) - - jnr-unixsocket (com.github.jnr:jnr-unixsocket:0.33 - http://github.com/jnr/jnr-unixsocket) - FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/) - Gson (com.google.code.gson:gson:2.8.6 - https://github.com/google/gson/gson) - Dagger (com.google.dagger:dagger:2.22 - https://github.com/google/dagger) @@ -70,19 +67,15 @@ Cryptomator uses 53 third-party dependencies under the following licenses: - javafx-fxml (org.openjfx:javafx-fxml:14 - https://openjdk.java.net/projects/openjfx/javafx-fxml/) - javafx-graphics (org.openjfx:javafx-graphics:14 - https://openjdk.java.net/projects/openjfx/javafx-graphics/) LGPL 2.1: - - dbus-java (com.github.hypfvieh:dbus-java:3.2.3 - https://github.com/hypfvieh/dbus-java/dbus-java) - jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix) - Java Native Access (net.java.dev.jna:jna:5.1.0 - https://github.com/java-native-access/jna) - Java Native Access Platform (net.java.dev.jna:jna-platform:5.1.0 - https://github.com/java-native-access/jna) MIT License: - java jwt (com.auth0:java-jwt:3.10.3 - https://github.com/auth0/java-jwt) - - java-utils (com.github.hypfvieh:java-utils:1.0.6 - https://github.com/hypfvieh/java-utils) - jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm) - jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined) - zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - https://github.com/nulab/zxcvbn4j) - - secret-service (de.swiesend:secret-service:1.1.0 - https://github.com/swiesend/secret-service) - Checker Qual (org.checkerframework:checker-qual:3.5.0 - https://checkerframework.org) - - kdewallet (org.purejava:kdewallet:1.1.1 - https://github.com/purejava/kdewallet) - SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - http://www.slf4j.org) The BSD 2-Clause License: - EasyBind (com.tobiasdiez:easybind:2.1.0 - https://github.com/tobiasdiez/EasyBind)