feat(ios): device client-cert mTLS — ClientTLS package, transport wiring, install UX (C-iOS)

- ios/Packages/ClientTLS: SecIdentity wrapper, PKCS12 importer (typed errors), keychain store
  (AfterFirstUnlockThisDeviceOnly), pure MutualTLSChallengeResponder truth table, cross-platform
  X.509 DER summary. 14/14 tests.
- Both transports (SessionCore URLSessionTermTransport, App URLSessionHTTPTransport) + SessionThumbnail
  take a lazy @Sendable ()->ClientIdentity? provider: WS resolves per-connect, HTTP per client-cert
  challenge, so a freshly-imported cert applies without an app relaunch. AppEnvironment injects
  { store.loadedIdentityOrNil() }.
- ClientCertScreen (.fileImporter([.pkcs12]) + passphrase -> import -> keychain), reachable via a
  设备证书 entry in SessionListScreen.hostMenu. PairingViewModel gates tunnel-host probes on cert
  presence and re-maps mTLS-reject to a clientCertRejected message.
Verified: ClientTLS 14/14, SessionCore 93/93, xcodegen + xcodebuild BUILD SUCCEEDED.
This commit is contained in:
Yaojia Wang
2026-07-07 09:42:12 +02:00
parent 5337281e85
commit e38e6d1689
25 changed files with 1510 additions and 30 deletions

View File

@@ -1,7 +1,9 @@
// swift-tools-version: 6.0
// T-iOS-1 scaffold shell. SessionEngine / ReconnectMachine / PingScheduler /
// GateState / AwayDigest / URLSessionTermTransport land in W1W2 tasks.
// Dependency direction is strictly downward: SessionCore WireProtocol only.
// Dependency direction is strictly downward: SessionCore WireProtocol, plus
// ClientTLS (C-iOS-2) for the device client-cert mTLS responder the WS transport
// answers connection-level challenges with. Both are downward leaves.
import PackageDescription
let package = Package(
@@ -12,12 +14,16 @@ let package = Package(
],
dependencies: [
.package(path: "../WireProtocol"),
.package(path: "../ClientTLS"),
.package(path: "../TestSupport"),
],
targets: [
.target(
name: "SessionCore",
dependencies: [.product(name: "WireProtocol", package: "WireProtocol")]
dependencies: [
.product(name: "WireProtocol", package: "WireProtocol"),
.product(name: "ClientTLS", package: "ClientTLS"),
]
),
.testTarget(
name: "SessionCoreTests",