Skip to content
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

Device Password fallback when Touch-ID devices are unavailable #11410

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from

Conversation

findus
Copy link

@findus findus commented Oct 24, 2024

Device Password fallback when Touch-ID devices are unavailable

I often use my macbook docked with the lid closed, so I cannot use the fingerprint sensor, and I got rather tired reentering my whole database password every time after unlocking my session.

This change adds the "kSecAccessControlDevicePasscode" flag to the AccessControlFlags, so it is possible to use the device password to unlock the database again.

This feature is disabled by default and can be enabled in the settings menu.

Screenshots

poc.mov

CleanShot 2024-10-25 at 11 52 24@2x

  • Touch-ID Hardware present, database locked
  • Unlock DB with Touch-ID sensor
  • Lid gets closed(no hardware available anymore)
  • Lock DB again
  • Password fallback kicks in after pressing unlock again

Type of change

  • ✅ New feature (change that adds functionality)

@findus findus marked this pull request as draft October 24, 2024 16:50
@findus findus marked this pull request as ready for review October 24, 2024 18:04
@droidmonkey
Copy link
Member

droidmonkey commented Oct 24, 2024

Oh neat, didn't even know that was an option. I don't think we need a special setting to toggle it though. Recommend removing that part and just making this part of the default security control options.

Whit this TouchID/WatchID is still possible when DB gets unlocked while no TouchID hardware is present.
@findus
Copy link
Author

findus commented Oct 25, 2024

@droidmonkey ok then I'll remove the settings menu entry again.

Having this default fallback, that is available all the time, we could also get rid of the isWatchIDAvailable/isTouchIdAvailable checks altogether.

Doing this TouchID/WatchID unlock would always be possbile, even when you open the database for the first time with no biometric/apple watch hardware present and open the lid later.

@findus findus force-pushed the feature/touchid-password-fallback branch from 2c28b5b to 0e80d00 Compare October 25, 2024 06:59
@findus findus force-pushed the feature/touchid-password-fallback branch from e68ee6a to 324fdd9 Compare October 25, 2024 07:34
@findus findus force-pushed the feature/touchid-password-fallback branch from 53cc096 to 3893c88 Compare October 25, 2024 07:53
@findus findus force-pushed the feature/touchid-password-fallback branch from 3893c88 to f675cd3 Compare October 25, 2024 07:56
@findus
Copy link
Author

findus commented Oct 25, 2024

Oh, its probably saver to keep all checks in place and add another one with https://developer.apple.com/documentation/localauthentication/klapolicydeviceownerauthentication, I'll do that later this day

With that we can add the "kSecAccessControlBiometryCurrentSet" Flag even though Biometry is unavailable due to closed lid or unpaired keyboard.

Adding this flag when TouchID is not enrolled results in an error when trying to save the secret.

The kSecAccessControlWatch Flag for apple watch compatibility does not have this limitation.

With that we can also offer quick unlock with only apple watch or password
@findus
Copy link
Author

findus commented Oct 25, 2024

It was not as easy as I thought, the kSecAccessControlBiometryCurrentSet flag is only allowed when TouchID is enrolled, adding the flag while its not enrolled results in an error when trying to save the secret to the keychain.

But it is possible to catch the specific "is not enrolled" error, and only skip adding the flag when this one occurs.

The kSecAccessControlWatch seems to not have this constraint, but I need to test it a little more, same as my current implementation for TouchID.

@findus
Copy link
Author

findus commented Oct 26, 2024

I tested i a little more and it seems to be pretty reliable, the only dangerous path is trying to save the key to the enclave with kSecAccessControlBiometryCurrentSet or kSecAccessControlTouchIDCurrentSet as the saving logic is throwing an error when having no fingers scanned, but that path is covered with the new touchidEnrolled check.

With this new logic it is now possible to:

  • Use TouchID in this constellations:
    • Docked Laptop, Lid Closed, no Magic Keyboard with TouchID paired -> Open Database -> Lock DB -> Open Lid -> TouchID available
    • Use WatchID Unlock even if TouchID is disabled
    • Use Password unlock exclusively when no other biometric or companion hardware is present
    • Use WatchID all the time even when pairing/unlocking the watch after unlocking Keepass for the first time

But I dont have an iMac or Mac Mini to test what happens on devices with no integrated TouchID, I tested this constellation in a qemu vm and this case also got handled by the kLAErrorBiometryNotEnrolled error, but I would feel saver if it could get tested with actual imac/mac mini hardware once.

I also could get my hands on a magic keyboard on monday to test that aswell at least once.

@droidmonkey
Copy link
Member

Awesome thank you @findus, this will greatly improve usability of quick unlock. Also helps when I finish implementing "remembered" quick unlock.

@findus
Copy link
Author

findus commented Oct 29, 2024

I've set up a Hackintosh yesterday to check what happens on devices with no TouchID present at all, there the "isEnrolled" check does not work because the API returns LAErrorBiometryNotAvailable, which kinda sucks because now this error is used for two states:

  • No Hardware present at all (Setkey fails all the time when the TouchID Flag is used)
  • Hardware not present because lid closed/magickeyboard unpaired (SetKey only fails when no fingers enrolled)

To "fix" this I just call setKey a second time without the TouchID/Biometrics flag when the first call fails, that makes it a little more robust.

@findus
Copy link
Author

findus commented Nov 5, 2024

I got my hand on an iMac and I tested the behavior if there is no TouchID hardware present, and what happens if you connect a magic keyboard with sensor to it.

It kinda behaves like on a Hackintosh, only the error message is different, here "kLAErrorBiometryNotPaired" is returned the first time setKey is called, the 2nd fallback call also enables us to use WatchId/DeviceCode.

After you pair the keyboard MacOS guides you through the fingerprint setup process, after that its the same behavior as with a macbook.

After you Unpair the keyboard again and delete your Fingerprints, the API returns "kLAErrorBiometryNotEnrolled" instead of "kLAErrorBiometryNotPaired" 🤔.

But in general its pretty stable now, I used this build for about a week now and haven't encountered anything weird.

@droidmonkey
Copy link
Member

Awesome and thank you for the comprehensive testing! Will merge this shortly.


bool canAuthenticate = [context canEvaluatePolicy:policyCode error:&error];
[context release];
//TODO: check if this is the correct error message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we satisfy this TODO?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also get rid of this check entirely now as we call setKey twice in case of an error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

… second time when first attempt fails with TouchID flag
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Hide option for TouchID when the TouchID scanner/button isn't accessible
2 participants