-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Touch ID provider #58
base: develop
Are you sure you want to change the base?
Changes from 2 commits
eabcbc9
f54f550
c909265
b1cdd16
f88d9d5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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(1000) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say, Touch ID has a higher Priority than Non-Touch ID. BUT
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Both is implemented. Please not, due to the higher priority, on new installations, Touch ID will be set up as the KeychainAccessProvider. |
||||||||||||||||||||||||
@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 true; | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be improved by calling native functions. See https://stackoverflow.com/questions/46887547/how-to-programmatically-check-support-of-face-id-and-touch-id and https://developer.apple.com/documentation/localauthentication/lacontext/biometrytype?language=objc There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but is this needed as we use Restricting this to devices with Touch ID would making access to keychain items more secure for owners of non Touch ID devices impossible. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the provider is called "Touch ID".^^ Also, i'm asking myself: As a user, do i expect to always enter my login pasword on every unlock, although i clicked the "save password" checkbox? @tobihagemann What are your thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @infeo. Users without Touch ID should not be forced to enter the login password on every unlock. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||
public boolean isLocked() { | ||||||||||||||||||||||||
return false; | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||
public void deletePassphrase(String key) throws KeychainAccessException { | ||||||||||||||||||||||||
keychain.deletePassword(SERVICE_NAME, key); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+64
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enhance error handling in deletePassphrase Similar to the changePassphrase method, deletePassphrase should throw an exception if the deletion fails. @Override
public void deletePassphrase(String key) throws KeychainAccessException {
- keychain.deletePassword(SERVICE_NAME, key);
+ if (!keychain.deletePassword(SERVICE_NAME, key)) {
+ throw new KeychainAccessException("Failed to delete passphrase");
+ }
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
@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); | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
Comment on lines
+69
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle failure in In the Apply this diff to handle the failure appropriately: @Override
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
- if (keychain.deletePassword(SERVICE_NAME, key)) {
+ if (!keychain.deletePassword(SERVICE_NAME, key)) {
+ throw new KeychainAccessException("Failed to delete existing passphrase.");
+ }
keychain.storePassword(SERVICE_NAME, key, passphrase, true);
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
org.cryptomator.macos.keychain.MacSystemKeychainAccess | ||
org.cryptomator.macos.keychain.MacSystemKeychainAccess | ||
org.cryptomator.macos.keychain.TouchIdKeychainAccess |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
org.cryptomator.macos.keychain.displayName=macOS Keychain | ||
org.cryptomator.macos.keychain.displayName=macOS Keychain | ||
org.cryptomator.macos.keychain.touchIdDisplayName=Touch ID | ||
infeo marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Both providers have the same priority level
Both TouchIdKeychainAccess and MacSystemKeychainAccess are annotated with
@Priority(1000)
. Since they have equal priority, the service loader's selection between them might be non-deterministic. Consider assigning different priority values to establish a clear preference order between these authentication methods.src/main/java/org/cryptomator/macos/keychain/TouchIdKeychainAccess.java
: Currently@Priority(1000)
src/main/java/org/cryptomator/macos/keychain/MacSystemKeychainAccess.java
: Currently@Priority(1000)
🔗 Analysis chain
Verify provider priority handling
The module now provides two KeychainAccessProvider implementations. We should ensure proper priority handling between TouchIdKeychainAccess and MacSystemKeychainAccess for correct fallback behavior.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 220
Script:
Length of output: 4072