Files
web-terminal/ios/Packages/ClientTLS/Tests/ClientTLSTests/EnrolledLeafFixtures.swift
Yaojia Wang a5fa843f00 test(ios): ClientTLS coverage 55.76% -> 89.49%, gate it, fix the three dead CI legs
ClientTLS was the most security-sensitive package in the tree and the least
covered, and it was not in the coverage gate at all (the gate's 4-package set
predates it). 48 -> 84 tests against the real macOS keychain, serialized with a
custom Testing trait after a @globalActor proved insufficient (actors yield at
await, so cross-await critical sections got interleaved by other cases' cleanup).

CI: the app/ipad/ios17 legs ran a bundle containing LiveServerSmokeTests, which
spawns tsx, with no npm ci -- a hard failure, not a skip, on a bare checkout.
Adds the missing iPad UI-test leg, and makes a missing iOS 17 runtime fail loudly
instead of silently reporting green.
2026-07-30 12:45:26 +02:00

104 lines
5.0 KiB
Swift

import Foundation
import Security
// B4 · Two real, DER-decodable leaf certificates for the enrolled-identity
// persistence tests, plus the cleanup they need.
//
// Both carry the SAME public key (the `KnownGoodCSR` fixed key) and the SAME
// subject, and differ only in serial + validity exactly the shape of a
// rotation: `SecItemAdd` treats them as two distinct items, while re-adding one
// of them byte-for-byte is `errSecDuplicateItem`.
//
// CLEANUP, and why it needs its own path: on macOS the file-based keychain
// OVERRIDES the `kSecAttrLabel` supplied at add time with the certificate's own
// subject summary (verified 2026-07-30: added under
// `test-XYZ.device-leaf`, found only under `enrolled-device-fixture`). So the
// production `KeychainClientIdentityStore` label-keyed delete cannot remove them
// on macOS, and the tests must sweep by subject instead `purgeFixtureLeaves()`
// runs both BEFORE (in case a previous run was killed) and AFTER every test that
// installs one. `SecItemDelete` removes ONE match per call here, hence the loop.
//
// Regenerated with (OpenSSL 3.0.18, key = KnownGoodCSR.privateKeyX963Base64):
// openssl req -x509 -new -key fixed.key.pem -subj "/CN=enrolled-device-fixture" \
// -days 3650 -sha256 -outform DER -out leaf1.der # then -days 3651 leaf2
// openssl req -x509 -new -key chain.key.pem \
// -subj "/CN=webterm-enrollment-ca-fixture" -days 3650 -sha256 -outform DER
enum EnrolledLeafFixtures {
/// The subject summary macOS files these certificates under.
static let subjectSummary = "enrolled-device-fixture"
static var leaf: Data { der(leafBase64) }
/// A second, DISTINCT leaf (different serial) for the rotation case.
static var rotatedLeaf: Data { der(rotatedLeafBase64) }
/// An issuer certificate for the stored `caChain`.
static var issuer: Data { der(issuerBase64) }
private static func der(_ base64: String) -> Data {
Data(base64Encoded: base64, options: .ignoreUnknownCharacters)!
}
private static let leafBase64 = """
MIIBmTCCAT+gAwIBAgIUcHFyXdi5QFelGKhdzcsbFlgnYzUwCgYIKoZIzj0EAwIwIjEgMB4GA1UE\
AwwXZW5yb2xsZWQtZGV2aWNlLWZpeHR1cmUwHhcNMjYwNzMwMDc1NTAxWhcNMzYwNzI3MDc1NTAx\
WjAiMSAwHgYDVQQDDBdlbnJvbGxlZC1kZXZpY2UtZml4dHVyZTBZMBMGByqGSM49AgEGCCqGSM49\
AwEHA0IABGInyJAZfMKCGjVNFg801yeq2Ar+auw1/4IC+g9tIf7Xa/VDY59VGBU9Vh5LYA7fLzs5\
6X1TF7ZCweW4mcaiJ9KjUzBRMB0GA1UdDgQWBBRAua2aufygDDq2CJTBF31OG7cNSDAfBgNVHSME\
GDAWgBRAua2aufygDDq2CJTBF31OG7cNSDAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gA\
MEUCIQCoLSypPlXvtMsQRMpztt/RpbYKc6iTmBLypqOZc0MxTAIgF/MVWpaLLYVCmcKBPxf7y9yQ\
NX3c6BrYB/fi/WzJsmA=
"""
private static let rotatedLeafBase64 = """
MIIBmTCCAT+gAwIBAgIUarD2zMEMcxZJNQevMMJenCAlLpkwCgYIKoZIzj0EAwIwIjEgMB4GA1UE\
AwwXZW5yb2xsZWQtZGV2aWNlLWZpeHR1cmUwHhcNMjYwNzMwMDc1NTAyWhcNMzYwNzI4MDc1NTAy\
WjAiMSAwHgYDVQQDDBdlbnJvbGxlZC1kZXZpY2UtZml4dHVyZTBZMBMGByqGSM49AgEGCCqGSM49\
AwEHA0IABGInyJAZfMKCGjVNFg801yeq2Ar+auw1/4IC+g9tIf7Xa/VDY59VGBU9Vh5LYA7fLzs5\
6X1TF7ZCweW4mcaiJ9KjUzBRMB0GA1UdDgQWBBRAua2aufygDDq2CJTBF31OG7cNSDAfBgNVHSME\
GDAWgBRAua2aufygDDq2CJTBF31OG7cNSDAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gA\
MEUCID2FFmKH2+qHB8sICyDuxQPtyj/wOLE24t4ghBEitLRvAiEAnTQQ2FP8Wei+8ITL/K57UP2Z\
YtYbEokKkBCj2bMr3vk=
"""
private static let issuerBase64 = """
MIIBpTCCAUugAwIBAgIUIkl7VDf6o9lBxtWxh7zFxPYTpo8wCgYIKoZIzj0EAwIwKDEmMCQGA1UE\
Awwdd2VidGVybS1lbnJvbGxtZW50LWNhLWZpeHR1cmUwHhcNMjYwNzMwMDc1NTAyWhcNMzYwNzI3\
MDc1NTAyWjAoMSYwJAYDVQQDDB13ZWJ0ZXJtLWVucm9sbG1lbnQtY2EtZml4dHVyZTBZMBMGByqG\
SM49AgEGCCqGSM49AwEHA0IABM4iWgauVe86+SKZg+jlHkh8VbVf70pgMdGOcpJW+xWb5oqfzxY3\
fW6pIdn3SCo9PG7X22qGtq/PLgpv97wtJvajUzBRMB0GA1UdDgQWBBTT9mdiVZTWcatuYz3NkxCb\
DxDSAjAfBgNVHSMEGDAWgBTT9mdiVZTWcatuYz3NkxCbDxDSAjAPBgNVHRMBAf8EBTADAQH/MAoG\
CCqGSM49BAMCA0gAMEUCIQCxfF8zyRHMW7nSmieDoBxIsOXkMFVPko86THW3TbwrUwIgMZlBP5rz\
FT4Y/G2oA+87xceEDjCrA7FnRZrCrZ4AADk=
"""
private static func labelQuery() -> [String: Any] {
[
kSecClass as String: kSecClassCertificate,
kSecAttrLabel as String: subjectSummary,
]
}
/// How many fixture leaves are currently installed.
static func installedCount() -> Int {
var query = labelQuery()
query[kSecReturnRef as String] = true
query[kSecMatchLimit as String] = kSecMatchLimitAll
var result: CFTypeRef?
guard SecItemCopyMatching(query as CFDictionary, &result) == errSecSuccess else {
return 0
}
if let array = result as? [Any] { return array.count }
return result == nil ? 0 : 1
}
/// Remove every installed fixture leaf. Idempotent; safe to call when none
/// are installed.
static func purgeFixtureLeaves() {
var guardCounter = 0
let maxSweeps = 16 // a leaf per enroll in a test; never unbounded
while SecItemDelete(labelQuery() as CFDictionary) == errSecSuccess,
guardCounter < maxSweeps {
guardCounter += 1
}
}
}