Skip to content

Commit

Permalink
Merge branch 'release/1.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
infeo committed Jan 10, 2025
2 parents 19d9923 + 68ba917 commit a930c10
Show file tree
Hide file tree
Showing 56 changed files with 309 additions and 152 deletions.
28 changes: 14 additions & 14 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>integrations-mac</artifactId>
<version>1.2.4</version>
<version>1.3.0</version>

<name>Cryptomator Integrations for macOS</name>
<description>Provides optional macOS services used by Cryptomator</description>
Expand All @@ -30,16 +30,16 @@
<project.jdk.version>17</project.jdk.version>

<!-- runtime dependencies -->
<api.version>1.3.1</api.version>
<slf4j.version>2.0.13</slf4j.version>
<api.version>1.5.0</api.version>
<slf4j.version>2.0.16</slf4j.version>

<!-- test dependencies -->
<junit.jupiter.version>5.10.2</junit.jupiter.version>
<mockito.version>5.12.0</mockito.version>
<junit.jupiter.version>5.11.4</junit.jupiter.version>
<mockito.version>5.15.2</mockito.version>

<!-- build plugin dependencies -->
<dependency-check.version>9.2.0</dependency-check.version>
<nexus-staging.version>1.6.14</nexus-staging.version>
<dependency-check.version>11.1.1</dependency-check.version>
<nexus-staging.version>1.7.0</nexus-staging.version>
</properties>

<licenses>
Expand Down Expand Up @@ -88,7 +88,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.3.2</version>
<version>3.4.0</version>
<configuration>
<filesets>
<fileset>
Expand Down Expand Up @@ -145,7 +145,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.3.0</version>
<version>3.5.0</version>
<executions>
<execution>
<goals>
Expand Down Expand Up @@ -199,7 +199,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<version>3.5.2</version>
</plugin>
<plugin>
<artifactId>maven-source-plugin</artifactId>
Expand All @@ -215,7 +215,7 @@
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.7.0</version>
<version>3.11.2</version>
<executions>
<execution>
<id>attach-javadocs</id>
Expand Down Expand Up @@ -307,7 +307,7 @@
<plugins>
<plugin>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.2.4</version>
<version>3.2.7</version>
<executions>
<execution>
<id>sign-artifacts</id>
Expand Down Expand Up @@ -368,10 +368,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.2</version>
<version>3.1.3</version>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
</project>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.macos.autostart.MacAutoStartProvider;
import org.cryptomator.macos.keychain.MacSystemKeychainAccess;
import org.cryptomator.macos.keychain.TouchIdKeychainAccess;
import org.cryptomator.macos.revealpath.OpenCmdRevealPathService;
import org.cryptomator.macos.tray.MacTrayIntegrationProvider;
import org.cryptomator.macos.uiappearance.MacUiAppearanceProvider;
Expand All @@ -14,7 +15,7 @@
requires org.slf4j;

provides AutoStartProvider with MacAutoStartProvider;
provides KeychainAccessProvider with MacSystemKeychainAccess;
provides KeychainAccessProvider with MacSystemKeychainAccess, TouchIdKeychainAccess;
provides RevealPathService with OpenCmdRevealPathService;
provides TrayIntegrationProvider with MacTrayIntegrationProvider;
provides UiAppearanceProvider with MacUiAppearanceProvider;
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/org/cryptomator/macos/keychain/MacKeychain.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ class MacKeychain {
/**
* Associates the specified password with the specified key in the system keychain.
*
* @param serviceName Service name
* @param account Unique account identifier
* @param password Passphrase to store
* @param serviceName Service name
* @param account Unique account identifier
* @param password Passphrase to store
* @param requireOsAuthentication Defines, whether the user needs to authenticate to store a passphrase
* @see <a href="https://developer.apple.com/documentation/security/1398366-seckeychainaddgenericpassword">SecKeychainAddGenericPassword</a>
*/
public void storePassword(String serviceName, String account, CharSequence password) throws KeychainAccessException {
public void storePassword(String serviceName, String account, CharSequence password, boolean requireOsAuthentication) throws KeychainAccessException {
ByteBuffer pwBuf = UTF_8.encode(CharBuffer.wrap(password));
byte[] pwBytes = new byte[pwBuf.remaining()];
pwBuf.get(pwBytes);
int errorCode = Native.INSTANCE.storePassword(serviceName.getBytes(UTF_8), account.getBytes(UTF_8), pwBytes);
int errorCode = Native.INSTANCE.storePassword(serviceName.getBytes(UTF_8), account.getBytes(UTF_8), pwBytes, requireOsAuthentication);
Arrays.fill(pwBytes, (byte) 0x00);
Arrays.fill(pwBuf.array(), (byte) 0x00);
if (errorCode != OSSTATUS_SUCCESS) {
Expand Down Expand Up @@ -75,7 +76,7 @@ private boolean tryMigratePassword(String account) {
if (pwBytes == null) {
return false;
}
int errorCode = Native.INSTANCE.storePassword(newServiceName, account.getBytes(UTF_8), pwBytes);
int errorCode = Native.INSTANCE.storePassword(newServiceName, account.getBytes(UTF_8), pwBytes, false);
Arrays.fill(pwBytes, (byte) 0x00);
if (errorCode != OSSTATUS_SUCCESS) {
return false;
Expand Down Expand Up @@ -103,6 +104,15 @@ public boolean deletePassword(String serviceName, String account) throws Keychai
}
}

/**
* Tests whether biometric authentication via Touch ID is supported and allowed on the device
*
* @return <code>true</code> if biometric authentication is available, <code>false</code> otherwise
*/
public boolean isTouchIDavailable() {
return Native.INSTANCE.isTouchIDavailable();
}

// initialization-on-demand pattern, as loading the .dylib is an expensive operation
private static class Native {
static final Native INSTANCE = new Native();
Expand All @@ -111,11 +121,13 @@ private Native() {
NativeLibLoader.loadLib();
}

public native int storePassword(byte[] service, byte[] account, byte[] value);
public native int storePassword(byte[] service, byte[] account, byte[] value, boolean requireOsAuthentication);

public native byte[] loadPassword(byte[] service, byte[] account);

public native int deletePassword(byte[] service, byte[] account);

public native boolean isTouchIDavailable();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ public String displayName() {

@Override
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
keychain.storePassword(SERVICE_NAME, key, passphrase);
keychain.storePassword(SERVICE_NAME, key, passphrase, false);
}

@Override
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean requireOsAuthentication) throws KeychainAccessException {
keychain.storePassword(SERVICE_NAME, key, passphrase, requireOsAuthentication);
}

@Override
Expand All @@ -62,7 +67,7 @@ public void deletePassphrase(String key) throws KeychainAccessException {
@Override
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
if (keychain.deletePassword(SERVICE_NAME, key)) {
keychain.storePassword(SERVICE_NAME, key, passphrase);
keychain.storePassword(SERVICE_NAME, key, passphrase, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.cryptomator.macos.keychain;

import org.cryptomator.integrations.common.OperatingSystem;
import org.cryptomator.integrations.common.Priority;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.macos.common.Localization;

/**
* Stores passwords in the macOS system keychain. Requires an authenticated user to do so.
* Authentication is done via TouchID or password as a fallback, when TouchID is not available.
* <p>
* Items are stored in the default keychain with the service name <code>Cryptomator</code>, unless configured otherwise
* using the system property <code>cryptomator.integrationsMac.keychainServiceName</code>.
*/
@Priority(1010)
@OperatingSystem(OperatingSystem.Value.MAC)
public class TouchIdKeychainAccess implements KeychainAccessProvider {

private static final String SERVICE_NAME = System.getProperty("cryptomator.integrationsMac.keychainServiceName", "Cryptomator");

private final MacKeychain keychain;

public TouchIdKeychainAccess() {
this(new MacKeychain());
}

// visible for testing
TouchIdKeychainAccess(MacKeychain keychain) {
this.keychain = keychain;
}

@Override
public String displayName() {
return Localization.get().getString("org.cryptomator.macos.keychain.touchIdDisplayName");
}

@Override
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
keychain.storePassword(SERVICE_NAME, key, passphrase, true);
}

@Override
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean requireOsAuthentication) throws KeychainAccessException {
keychain.storePassword(SERVICE_NAME, key, passphrase, requireOsAuthentication);
}

@Override
public char[] loadPassphrase(String key) {
return keychain.loadPassword(SERVICE_NAME, key);
}

@Override
public boolean isSupported() {
return keychain.isTouchIDavailable();
}

@Override
public boolean isLocked() {
return false;
}

@Override
public void deletePassphrase(String key) throws KeychainAccessException {
keychain.deletePassword(SERVICE_NAME, key);
}

@Override
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
if (keychain.deletePassword(SERVICE_NAME, key)) {
keychain.storePassword(SERVICE_NAME, key, passphrase, true);
}
}

}
Loading

0 comments on commit a930c10

Please sign in to comment.