Commit Graph
9 Commits
Author SHA1 Message Date
Ruslan Nigmatullin 8d431c0c50 app-server: require keychain entitlements before macOS fallback 2026-04-23 21:58:51 -07:00
Ruslan Nigmatullin 972149926d Handle missing entitlement for macOS device keys 2026-04-23 21:58:51 -07:00
Ruslan Nigmatullin 6fd3bf8bbe Move macOS device key provider to Objective-C 2026-04-23 21:58:51 -07:00
Ruslan Nigmatullin 9b592ee45d codex: fix CI failure on PR #18431
## Summary

Add the required parameter-name comment for the None authentication-context argument in the macOS device-key lookup path. CI macOS argument-comment lint enforces these comments for positional literal-like arguments.

## Validation

- just fmt
- cargo test -p codex-device-key
- just fix -p codex-device-key
- bazel build --config=argument-comment-lint //codex-rs/device-key:device-key-unit-tests-bin
- git diff --check

Note: a full local just argument-comment-lint run reached the fixed device-key target, then failed later in an unrelated webrtc-sys build script with a stale sandbox path outside this PR changes.
2026-04-23 21:58:51 -07:00
Ruslan Nigmatullin 4785b945e8 codex: fix CI failure on PR #18431
## Summary

Load LocalAuthentication.framework at runtime before constructing LAContext instead of linking the framework into every macOS target. The previous direct framework link made unrelated macOS Bazel targets link against an SDK-backed framework path, which caused the macOS CI jobs to fail during linking.

The signing flow still uses LAContext for authenticated private-key operations; this only changes how the framework becomes available to the Objective-C runtime.

## Validation

- just fmt
- cargo test -p codex-device-key
- just fix -p codex-device-key
- git diff --check
- bazel build //codex-rs/device-key:device-key
2026-04-23 21:58:50 -07:00
Ruslan Nigmatullin 08b0d83b66 app-server: require local auth for macOS device keys
Require macOS-created device keys to prove user presence before private-key use by adding the UserPresence access-control flag at key creation time. This keeps the provider aligned with the device-ownership goal: a valid signature should require both the device-local private key and a successful local biometric or password authentication.

Thread a process-local LAContext into signing lookups and reuse it across sign operations. Security.framework still owns the authentication policy and validity window, while the reusable context lets macOS reuse a recent successful authentication when policy permits instead of prompting on every connection.

Public-key lookup and binding reads continue to avoid the authentication context because they do not use the private key.

- just fmt
- cargo test -p codex-device-key
- just fix -p codex-device-key
- git diff --check
2026-04-23 21:58:50 -07:00
Ruslan Nigmatullin 72a6cab442 app-server: add macOS device key provider
The device-key crate needs a platform provider that can keep private keys non-extractable on macOS while preserving the crate-level protection policy and structured signing boundary.

macOS has two useful local protection classes for this API: hardware-backed Secure Enclave keys when available, and OS-protected non-extractable keys as an explicit fallback. Reporting which class was selected keeps that distinction visible to callers.

The Security.framework access-control flags used here provide device-local non-exportability and continuity while the user session is unlocked. They do not require UserPresence or Biometry for each signature, so the key itself is not treated as a complete same-controller compromise boundary; the API still relies on local-transport restriction, app-server authorization, and structured payload validation.

- Added the macOS DeviceKeyProvider implementation backed by Security.framework.
- Created and loaded P-256 private keys by stable device-key ID.
- Preferred Secure Enclave keys by default and allowed OS-protected non-extractable fallback only when requested by policy.
- Returned SPKI DER public keys and DER-encoded ECDSA signatures through the existing crate API.
- Documented the macOS provider threat-model boundary around this-device-only keys without UserPresence or Biometry flags.
- Added macOS-only Security.framework and CoreFoundation dependencies.

- just fmt
- cargo test -p codex-device-key
- just fix -p codex-device-key
- git diff --check
- just bazel-lock-update
- just bazel-lock-check
2026-04-23 21:58:50 -07:00
Ruslan Nigmatullin 19badb0be2 app-server: persist device key bindings in sqlite (#19206)
## Why

Device-key providers should only own platform key material. The
account/client binding used to authorize a signing payload is app-server
state, and keeping that state in provider-specific metadata makes the
same check harder to audit and harder to share across platform
implementations.

Persisting the binding in the shared state database gives the device-key
crate a platform-neutral source of truth before it asks a provider to
sign. It also lets app-server move potentially blocking key operations
off the main message processor path, which matters once providers may
wait for OS authentication prompts.

## What changed

- Add a `device_key_bindings` state migration plus `StateRuntime`
helpers keyed by `key_id`.
- Add an async `DeviceKeyBindingStore` abstraction to `codex-device-key`
and use it from `DeviceKeyStore::create` and `DeviceKeyStore::sign`.
- Keep provider calls behind async store methods and run the synchronous
provider work through `spawn_blocking`.
- Wire app-server device-key RPC handling to the SQLite-backed binding
store and spawn response/error delivery tasks for device-key requests.
- Run the turn-start tracing test on the existing larger current-thread
test harness after the larger async surface made the default test stack
too small locally.

## Validation

- `cargo test -p codex-device-key`
- `cargo test -p codex-state device_key`
- `cargo test -p codex-state`
- `cargo test -p codex-app-server device_key`
- `cargo test -p codex-app-server
message_processor::tracing_tests::turn_start_jsonrpc_span_parents_core_turn_spans`
- `cargo test -p codex-app-server`
- `just fix -p codex-device-key`
- `just fix -p codex-state`
- `just fix -p codex-app-server`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `git diff --check`
2026-04-23 21:55:56 -07:00
Ruslan Nigmatullin 5bab04dcd7 app-server: add codex-device-key crate (#18429)
## Why

Device-key storage and signing are local security-sensitive operations
with platform-specific behavior. Keeping the core API in
`codex-device-key` keeps app-server focused on routing and business
logic instead of owning key-management details.

The crate keeps the signing surface intentionally narrow: callers can
create a bound key, fetch its public key, or sign one of the structured
payloads accepted by the crate. It does not expose a generic
arbitrary-byte signing API.

Key IDs cross into platform-specific labels, tags, and metadata paths,
so externally supplied IDs are constrained to the same auditable
namespace created by the crate: `dk_` followed by unpadded base64url for
32 bytes. Remote-control target paths are also tied to each signed
payload shape so connection proofs cannot be reused for enrollment
endpoints, or vice versa.

## What changed

- Added the `codex-device-key` workspace crate.
- Added account/client-bound key creation with stable `dk_` key IDs.
- Added strict `key_id` validation before public-key lookup or signing
reaches a provider.
- Added public-key lookup and structured signing APIs.
- Split remote-control client endpoint allowlists by connection vs
enrollment payload shape.
- Added validation for key bindings, accepted payload fields, token
expiration, and payload/key binding mismatches.
- Added flow-oriented docs on the validation helpers that gate provider
signing.
- Added protection policy and protection-class types without wiring a
platform provider yet.
- Added an unsupported default provider so platforms without an
implementation fail explicitly instead of silently falling back to
software-backed keys.
- Updated Cargo and Bazel lock metadata for the new crate and
non-platform-specific dependencies.

## Stack

This is stacked on #18428.

## Validation

- `cargo test -p codex-device-key`
- Added unit coverage for strict `key_id` validation before provider
use.
- Added unit coverage that rejects remote-control paths from the wrong
signed payload shape.
- `just bazel-lock-update`
- `just bazel-lock-check`
2026-04-21 17:57:00 +00:00