diff --git a/main/keychain/pom.xml b/main/keychain/pom.xml new file mode 100644 index 000000000..4fab0ebc6 --- /dev/null +++ b/main/keychain/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + org.cryptomator + main + 1.2.0-SNAPSHOT + + keychain + System Keychain Access + + + + org.apache.commons + commons-lang3 + + + org.bouncycastle + bcprov-jdk15on + 1.54 + + + + + com.google.dagger + dagger + + + com.google.dagger + dagger-compiler + provided + + + + + org.cryptomator + commons-test + + + \ No newline at end of file diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccess.java new file mode 100644 index 000000000..af2152ce6 --- /dev/null +++ b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccess.java @@ -0,0 +1,19 @@ +package org.cryptomator.keychain; + +public interface KeychainAccess { + + /** + * 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); + + /** + * @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. + */ + CharSequence loadPassphrase(String key); + +} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java new file mode 100644 index 000000000..b304d6edf --- /dev/null +++ b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainAccessStrategy.java @@ -0,0 +1,10 @@ +package org.cryptomator.keychain; + +interface KeychainAccessStrategy extends KeychainAccess { + + /** + * @return true if this KeychainAccessStrategy works on the current machine. + */ + boolean isSupported(); + +} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainComponent.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainComponent.java new file mode 100644 index 000000000..0264aa3fc --- /dev/null +++ b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainComponent.java @@ -0,0 +1,15 @@ +package org.cryptomator.keychain; + +import java.util.Optional; + +import javax.inject.Singleton; + +import dagger.Component; + +@Singleton +@Component(modules = KeychainModule.class) +public interface KeychainComponent { + + Optional keychainAccess(); + +} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java new file mode 100644 index 000000000..ab593f674 --- /dev/null +++ b/main/keychain/src/main/java/org/cryptomator/keychain/KeychainModule.java @@ -0,0 +1,26 @@ +package org.cryptomator.keychain; + +import java.util.Optional; +import java.util.Set; + +import com.google.common.collect.Sets; + +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.ElementsIntoSet; + +@Module +public class KeychainModule { + + @Provides + @ElementsIntoSet + Set provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsSystemKeychainAccess winKeychain) { + return Sets.newHashSet(macKeychain, winKeychain); + } + + @Provides + public Optional provideSupportedKeychain(Set keychainAccessStrategies) { + return keychainAccessStrategies.stream().filter(KeychainAccessStrategy::isSupported).map(KeychainAccess.class::cast).findFirst(); + } + +} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java new file mode 100644 index 000000000..55a5e6ce9 --- /dev/null +++ b/main/keychain/src/main/java/org/cryptomator/keychain/MacSystemKeychainAccess.java @@ -0,0 +1,45 @@ +package org.cryptomator.keychain; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.apache.commons.lang3.SystemUtils; + +@Singleton +class MacSystemKeychainAccess implements KeychainAccessStrategy { + + private final KeyStore keyStore; + + @Inject + public MacSystemKeychainAccess() { + KeyStore ks; + try { + ks = KeyStore.getInstance("KeychainStore", "Apple"); + ks.load(null); + } catch (GeneralSecurityException | IOException e) { + ks = null; + } + this.keyStore = ks; + } + + @Override + public void storePassphrase(String key, CharSequence passphrase) { + // TODO Auto-generated method stub + } + + @Override + public CharSequence loadPassphrase(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isSupported() { + return SystemUtils.IS_OS_MAC_OSX && keyStore != null; + } + +} diff --git a/main/keychain/src/main/java/org/cryptomator/keychain/WindowsSystemKeychainAccess.java b/main/keychain/src/main/java/org/cryptomator/keychain/WindowsSystemKeychainAccess.java new file mode 100644 index 000000000..61aed5af9 --- /dev/null +++ b/main/keychain/src/main/java/org/cryptomator/keychain/WindowsSystemKeychainAccess.java @@ -0,0 +1,45 @@ +package org.cryptomator.keychain; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.apache.commons.lang3.SystemUtils; + +@Singleton +class WindowsSystemKeychainAccess implements KeychainAccessStrategy { + + private final KeyStore keyStore; + + @Inject + public WindowsSystemKeychainAccess() { + KeyStore ks; + try { + ks = KeyStore.getInstance("Windows-MY", "SunMSCAPI"); + ks.load(null); + } catch (GeneralSecurityException | IOException e) { + ks = null; + } + this.keyStore = ks; + } + + @Override + public void storePassphrase(String key, CharSequence passphrase) { + // TODO Auto-generated method stub + } + + @Override + public CharSequence loadPassphrase(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isSupported() { + return SystemUtils.IS_OS_WINDOWS && keyStore != null; + } + +} diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java b/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java new file mode 100644 index 000000000..c15c8c1f1 --- /dev/null +++ b/main/keychain/src/test/java/org/cryptomator/keychain/KeychainModuleTest.java @@ -0,0 +1,17 @@ +package org.cryptomator.keychain; + +import java.util.Optional; + +import org.junit.Assert; +import org.junit.Test; + +public class KeychainModuleTest { + + @Test + public void testGetKeychain() { + Optional keychainAccess = DaggerKeychainComponent.builder().keychainModule(new KeychainTestModule()).build().keychainAccess(); + Assert.assertTrue(keychainAccess.isPresent()); + Assert.assertTrue(keychainAccess.get() instanceof MapKeychainAccess); + } + +} diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/KeychainTestModule.java b/main/keychain/src/test/java/org/cryptomator/keychain/KeychainTestModule.java new file mode 100644 index 000000000..e2636981b --- /dev/null +++ b/main/keychain/src/test/java/org/cryptomator/keychain/KeychainTestModule.java @@ -0,0 +1,14 @@ +package org.cryptomator.keychain; + +import java.util.Set; + +import com.google.common.collect.Sets; + +public class KeychainTestModule extends KeychainModule { + + @Override + Set provideKeychainAccessStrategies(MacSystemKeychainAccess macKeychain, WindowsSystemKeychainAccess winKeychain) { + return Sets.newHashSet(new MapKeychainAccess()); + } + +} diff --git a/main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java b/main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java new file mode 100644 index 000000000..0af7f0a51 --- /dev/null +++ b/main/keychain/src/test/java/org/cryptomator/keychain/MapKeychainAccess.java @@ -0,0 +1,25 @@ +package org.cryptomator.keychain; + +import java.util.HashMap; +import java.util.Map; + +class MapKeychainAccess implements KeychainAccessStrategy { + + private final Map map = new HashMap<>(); + + @Override + public void storePassphrase(String key, CharSequence passphrase) { + map.put(key, passphrase); + } + + @Override + public CharSequence loadPassphrase(String key) { + return map.get(key); + } + + @Override + public boolean isSupported() { + return true; + } + +} diff --git a/main/pom.xml b/main/pom.xml index 6640becc7..ef7cf60fb 100644 --- a/main/pom.xml +++ b/main/pom.xml @@ -273,6 +273,7 @@ frontend-webdav ui filesystem-charsets + keychain