You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Prove non-exportable key custody on a real keychain (BEP, Gatehouse §6.10) #2436
Residual of #2363 (T2, epic #1501). Named by minimal-arch condition C2 on 909b8a7.
Every test in crates/bep/src/keys.rs runs against keys::memory::MemoryStore. The macOS lane now compiles keys::keychain (justfile scope carries -p bep) but nothing exercises it. Gatehouse §6.10 lines 837 and 839 require hardware-backed, non-exportable custody for the sealing and root keys.
Done when a proof runs on a real keychain on the macOS runner and shows: a generated key is not exportable (kSecAttrIsExtractable false), a key the store lost while another role's key is held gives the replacement diagnostic and never regenerates silently, and the Linux backend that OQ4 leaves out (crates/bep/src/main.rs line 49) has an equivalent proof or a recorded decision.
Ran a probe on macOS 26.7 (arm64). It uses the same key attributes as crates/bep/src/keys.rsgenerate(): P-256, permanent, kSecAttrIsExtractable=false. The probe was an unsigned swift script.
keychain
result
legacy login keychain (what keys.rs uses today)
the key is created, and SecKeyCopyExternalRepresentation returns its bytes, so it is exportable
generation fails with -34018 (errSecMissingEntitlement)
What this means:
The legacy keychain does not enforce non-exportable for the process that made the key. keys.rs:653 refuses such a key, so every bep start on this host fails with "the keychain made the key exportable". That refusal is correct. No change to the code inside generate() fixes it.
The data-protection keychain, or the Secure Enclave, needs a keychain-access-groups entitlement. On macOS that is a restricted entitlement: it needs a Developer ID signature and an embedded provisioning profile. An ad-hoc build cannot get it.
Not probed: whether a Developer ID-signed binary changes the legacy-keychain result.
Consequence for the proof this issue asks for: a real-keychain key-custody proof needs a signed bep with that entitlement. That ties this issue to the release-signing task #2770.
Separate defect: bep --replace-key <role> deletes the old key before it generates the new one. When generation fails, the host has no key at all, and the message tells the user to run the same command again, which cannot succeed.
Order changed (owner decision, 2026-10-31): this issue now waits on #2770 (T101, build, sign and notarize bep in the macOS release), not the other way round. The plan comment on #1501 and #2770's body are updated.
Why: the probe on this issue needs code signed with the Developer ID. None of the nightly binaries that are signed today creates a SecKey. min's client key is a 32-byte secret stored as a keychain item. Only bep generates a key with kSecAttrIsExtractable=false, and the release does not ship bep yet. So the first Developer-ID-signed bep that #2770 ships is itself the probe.
Proof for this issue: on a stock Mac, install the release or nightly that first carries the signed bep-macos-arm64, and start it (bep serve, or a GitHub grant box through min). One of two outcomes:
It starts with no "the keychain made the key exportable" refusal. Then the login keychain keeps a signed program's keys non-exportable, and this issue closes on that run.
It refuses. Then signing alone is not enough, and the custody fix (the data-protection keychain with its keychain-access-groups entitlement and a provisioning profile) lands before T102 #2772.
Until then, debug builds keep an exportable key with a warning (T111 #2785), so dev Macs can run the live legs.
Probe result: the signed bep is refused. Signing alone does not keep the key non-exportable.
Binary: bep-macos-arm64 from release run 41181733187 (branch feat/t101-release-bep, feat(release): ship a signed, notarized bep beside minvmd on macOS #2792). Developer ID Application, TeamIdentifier 3G47C5HY64, hardened runtime (flags=0x10000(runtime)), notarized (spctl: "accepted source=Notarized Developer ID"). No entitlements.
WARN bep::keys: removed a proxy key and generated no new key role=sealing error=the key store failed for the sealing key: the keychain made the key exportable
The old sealing key was deleted, but no new key was made: the key store failed for the sealing key: the keychain made the key exportable. Run `bep --replace-key sealing` again.
So the custody check (KeyError::Exportable) fired on a key that a Developer-ID-signed, notarized bep generated. Per the decision recorded above, the custody fix lands before T102 (#2772): the data-protection keychain with a keychain-access-groups entitlement and a provisioning profile.
This binary predates #2801, so the old --replace-key deleted the old key before it generated the new one. That is the ordering #2801 fixes.
Custody fix filed as #2812: Secure Enclave keys, with bep bundled under a Developer ID provisioning profile. Its last step re-runs this probe on the bundled release bep, which closes this issue for macOS. The Linux backend (OQ4) stays here.
macOS part done. The probe passes on the bundled release bep (0.8.0-dev.381.g3c2b2a73): no exportable refusal, and --replace-key exits 0 for all three roles. The result is on #2812.
This issue stays open for the Linux backend (OQ4): it still needs an equivalent proof or a recorded decision.
Activity
norrietaylor commentedon Oct 31, 2026
Ran a probe on macOS 26.7 (arm64). It uses the same key attributes as
crates/bep/src/keys.rsgenerate(): P-256, permanent,kSecAttrIsExtractable=false. The probe was an unsignedswiftscript.keys.rsuses today)SecKeyCopyExternalRepresentationreturns its bytes, so it is exportablekSecUseDataProtectionKeychain)-34018(errSecMissingEntitlement)What this means:
keys.rs:653refuses such a key, so everybepstart on this host fails with "the keychain made the key exportable". That refusal is correct. No change to the code insidegenerate()fixes it.keychain-access-groupsentitlement. On macOS that is a restricted entitlement: it needs a Developer ID signature and an embedded provisioning profile. An ad-hoc build cannot get it.Consequence for the proof this issue asks for: a real-keychain key-custody proof needs a signed
bepwith that entitlement. That ties this issue to the release-signing task #2770.Separate defect:
bep --replace-key <role>deletes the old key before it generates the new one. When generation fails, the host has no key at all, and the message tells the user to run the same command again, which cannot succeed.norrietaylor commentedon Oct 31, 2026
Order changed (owner decision, 2026-10-31): this issue now waits on #2770 (T101, build, sign and notarize
bepin the macOS release), not the other way round. The plan comment on #1501 and #2770's body are updated.Why: the probe on this issue needs code signed with the Developer ID. None of the nightly binaries that are signed today creates a
SecKey.min's client key is a 32-byte secret stored as a keychain item. Onlybepgenerates a key withkSecAttrIsExtractable=false, and the release does not shipbepyet. So the first Developer-ID-signedbepthat #2770 ships is itself the probe.Proof for this issue: on a stock Mac, install the release or nightly that first carries the signed
bep-macos-arm64, and start it (bep serve, or a GitHub grant box throughmin). One of two outcomes:keychain-access-groupsentitlement and a provisioning profile) lands before T102 #2772.Until then, debug builds keep an exportable key with a warning (T111 #2785), so dev Macs can run the live legs.
norrietaylor commentedon Oct 31, 2026
Probe result: the signed
bepis refused. Signing alone does not keep the key non-exportable.bep-macos-arm64from release run 41181733187 (branchfeat/t101-release-bep, feat(release): ship a signed, notarized bep beside minvmd on macOS #2792). Developer ID Application, TeamIdentifier 3G47C5HY64, hardened runtime (flags=0x10000(runtime)), notarized (spctl: "accepted source=Notarized Developer ID"). No entitlements.bep-macos-arm64 --replace-key sealing --identity <temp>/identity.jsonSo the custody check (
KeyError::Exportable) fired on a key that a Developer-ID-signed, notarizedbepgenerated. Per the decision recorded above, the custody fix lands before T102 (#2772): the data-protection keychain with akeychain-access-groupsentitlement and a provisioning profile.This binary predates #2801, so the old
--replace-keydeleted the old key before it generated the new one. That is the ordering #2801 fixes.norrietaylor commentedon Oct 31, 2026
Custody fix filed as #2812: Secure Enclave keys, with
bepbundled under a Developer ID provisioning profile. Its last step re-runs this probe on the bundled releasebep, which closes this issue for macOS. The Linux backend (OQ4) stays here.feat(release): ship a signed, notarized bep beside minvmd on macOS (g…
norrietaylor commentedon Nov 5, 2026
macOS part done. The probe passes on the bundled release
bep(0.8.0-dev.381.g3c2b2a73): no exportable refusal, and--replace-keyexits 0 for all three roles. The result is on #2812.This issue stays open for the Linux backend (OQ4): it still needs an equivalent proof or a recorded decision.
Generated by Claude Code