fix(ios): gate hot-key signing behind Face ID via .userPresence on device#299
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
.userPresenceto the iOS Secure Enclave hot-key access control so user-initiated hot signing (and hot-key reveal) require Face ID / Touch ID on real devices — closing an iOS-only gap where these operations signed silently.Also bundles a small Xcode-26 build fix (see end) that the iOS build needs to compile at all.
Why (
.userPresence)The SE hot key in
HotKeyPlugin.swiftwas created with.privateKeyUsageonly..privateKeyUsageis a usage permission — by itself it does not prompt for authentication (only.userPresence/.biometryAny/.biometryCurrentSet/.devicePasscodedo). The in-code comment claiming ".privateKeyUsagetriggers Face ID" was simply wrong.Consequences:
SecKeyCreateDecryptedDataunwrapped the secret with no prompt → user-initiated Guardian claims/sends signed silently, despite the design intent of a "tap-to-confirm biometric on mobile".HotKeyPlugin.ktusessetUserAuthenticationRequired(true)+ StrongBox and unwraps inside aBiometricPrompt. The two platforms had diverged.The change
generateHotKeynow builds the access-control flags as:.userPresence, not.biometryCurrentSet— both require the user to be present, but.biometryCurrentSetinvalidates the key on biometric re-enrollment (bricks the hot key until re-activation)..userPresencesurvives re-enrollment and falls back to the device passcode.#if !targetEnvironment(simulator)) — mirrors the existingkSecAttrTokenIDSecureEnclavesimulator guard. On the simulator the host SE is unavailable and biometrics aren't reliably enrolled, so we keep.privateKeyUsage-only there; this keeps the iOS E2E harness's silent signing working.Also corrects the two now-inaccurate code comments.
Scope / caveats
buildColdMultisigService) and never touches the hot key, so silent background claims keep working.#if !simulatorbranch isn't compiled/exercised in CI — it must be verified on a physical device.Also bundled: Xcode-26 iOS build fix
main's iOS build is currently broken under Xcode 26 (themacos-26CI runner): twofoundKey as? SecKeydowncasts (signWithHotKeyline ~246,revealHotKeyline ~352) are no-ops for CoreFoundation types — they always succeed — which Xcode 26 rejects as a hard error. Replaced each with aCFGetTypeID(foundKey) == SecKeyGetTypeID()guard + force-cast (the correct defensive downcast).This fix is byte-identical to the one already on #248, so the two won't conflict when both land. Bundling it here lets this PR's iOS build go green and unbreaks main's iOS release build independently of #248's timeline.
Verification
xcrun --sdk iphoneos swiftc -typecheck(compiles the.userPresencebranch) and--sdk iphonesimulatorboth exit 0.build-mobile.yml(iOS Simulator, macos-26) on this branch: the first run revealed the pre-existingas? SecKeybreak (Android passed); re-run after the bundled fix confirms the full iOS project compiles.Android already enforces the biometric gate; no Android change.