Skip to content

feat(mac): persist device identity in keychain - #6

Merged
zls233 merged 1 commit into
mainfrom
task/mac-device-identity
Sep 2, 2026
Merged

feat(mac): persist device identity in keychain#6
zls233 merged 1 commit into
mainfrom
task/mac-device-identity

Conversation

@zls233

@zls233 zls233 commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the first Mac-side Phase 2 identity foundation: one persistent P-256 private key in Keychain, a protocol-compatible public identity envelope, and DER ECDSA transcript signing. This slice does not wire identity into Bridge routes or pairing UI.

Changes

  • Adds an internal TouchCodeIdentity SwiftPM target used by the Mac executable.
  • Loads or creates a permanent P-256 private key under a versioned Keychain application tag.
  • Derives X9.63 public key and tcid1_ device ID using the shared protocol domain separator.
  • Exposes only public identity metadata and signing; private key material is never exported through the API.
  • Serializes repeated/concurrent access, retries cleanly after transient creation failure, and fails closed for malformed stored keys.
  • Adds deterministic tests using in-memory Security.framework keys, including the shared TypeScript golden vector.

Validation

  • DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer swift test --scratch-path /tmp/touchcode-mac-device-identity.v64cbr — 7/7 passed.
  • DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer swift build --scratch-path /tmp/touchcode-mac-device-identity.v64cbr — passed.
  • DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer ./script/build_and_run.sh verify — passed; existing --build-system native deprecation warning only.
  • git diff --check origin/main...HEAD — passed.

API Changes

Adds the internal Swift TouchCodeIdentity target with DeviceIdentity, DeviceIdentityStore, DeviceIdentityDerivation, and DeviceIdentityError. No Bridge HTTP or shared TypeScript protocol shape changes.

Risks / unresolved issues

  • The production Keychain provider is compiled but not exercised by default tests to avoid mutating the user Keychain. Persistence on a real signed Mac app remains unverified.
  • This uses Keychain-backed P-256 storage but does not claim Secure Enclave or hardware-backed non-exportability.
  • Bridge IPC/hello integration, challenge TTL/replay handling, trusted-peer persistence, SAS UI, iPad identity, and physical-device pairing remain later Phase 2 slices.
  • The default SwiftPM .build path in this FileProvider checkout acquired Finder attributes that broke test-bundle codesign; tests passed with an isolated /tmp scratch path.

Implementation complete: yes
Self-review complete: yes
Known remaining risks: production Keychain and end-to-end pairing are not yet verified.

Delivered branch SHA: f99bf344fe39961efb447401ab9dbdd8d3021d41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Protocol displayName validation can diverge cross-platform (Swift grapheme count vs JS UTF-16 length) and the Keychain-backed provider isn’t covered by tests.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a new macOS SwiftPM identity module that persists a device P-256 private key in Keychain, derives a protocol-compatible public identity envelope (including tcid1_ deviceId), and produces DER-encoded ECDSA signatures over transcripts.

Changes:

  • Introduces TouchCodeIdentity target with DeviceIdentityStore for load-or-create and signing using a Keychain-backed P-256 key.
  • Implements shared-protocol deviceId derivation (domain-separated SHA-256 over X9.63 public key) and base64url encoding.
  • Adds deterministic unit tests covering derivation, concurrency behavior, tamper detection, and failure/validation scenarios via in-memory key providers.
File summaries
File Description
apps/mac-app/Sources/TouchCodeIdentity/DeviceIdentityStore.swift New identity store + derivation utilities + Keychain key provider for persistent P-256 identity and DER signing.
apps/mac-app/Tests/TouchCodeIdentityTests/DeviceIdentityStoreTests.swift New tests for deviceId golden vector, stability across calls, concurrency, signature verification, and failure modes.
apps/mac-app/Package.swift Registers the new TouchCodeIdentity target, wires it into the mac executable, and adds a dedicated test target.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +139 to +142
let normalizedName = displayName.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalizedName.isEmpty, normalizedName.count <= 128 else {
throw DeviceIdentityError.invalidDisplayName
}
Comment on lines +69 to +77
func loadPrivateKey() throws -> SecKey? {
var result: CFTypeRef?
let status = SecItemCopyMatching([
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: applicationTag,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecReturnRef as String: true,
kSecMatchLimit as String: kSecMatchLimitOne,
] as CFDictionary, &result)
@zls233
zls233 merged commit 7ab827c into main Sep 2, 2026
1 check passed
@zls233
zls233 deleted the task/mac-device-identity branch September 2, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants