test: record partial native bridge import control

This commit is contained in:
Joseph Doherty
2026-11-10 17:28:30 -05:00
parent ce21f5db09
commit 2e4905f112
13 changed files with 1009 additions and 0 deletions
@@ -0,0 +1,95 @@
using System.Diagnostics;
using System.Collections;
using System.Reflection;
using System.Runtime;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using Opc.Ua;
using Opc.Ua.Configuration;
using Opc.Ua.Security.Certificates;
[System.Runtime.Versioning.SupportedOSPlatform("macos")]
internal static class ImportSeed
{
const string Uri = "urn:localhost:NativeRetention.Control";
static string Hash(Certificate cert) => Convert.ToHexString(SHA256.HashData(cert.RawData)).ToLowerInvariant();
static void Require(bool yes, string message) { if (!yes) throw new InvalidOperationException(message); }
static ApplicationConfiguration Configuration(string pki, ITelemetryContext telemetry) => new(telemetry)
{
ApplicationName = "NativeRetention.Control", ApplicationUri = Uri, ApplicationType = ApplicationType.Client,
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier { StoreType = CertificateStoreType.Directory,
StorePath = Path.Combine(pki, "own"), SubjectName = "CN=NativeRetention.Control" },
TrustedPeerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(pki, "trusted") },
TrustedIssuerCertificates = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(pki, "issuers") },
RejectedCertificateStore = new CertificateTrustList { StoreType = CertificateStoreType.Directory, StorePath = Path.Combine(pki, "rejected") },
AutoAcceptUntrustedCertificates = false,
},
ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 15000 },
};
static async Task<ApplicationInstance> Open(string pki, ITelemetryContext telemetry, CancellationToken ct)
{
var app = new ApplicationInstance(telemetry) { ApplicationType = ApplicationType.Client,
ApplicationName = "NativeRetention.Control", ApplicationConfiguration = Configuration(pki, telemetry) };
try
{
await app.ApplicationConfiguration!.ValidateAsync(ApplicationType.Client, ct);
Require(await app.CheckApplicationInstanceCertificatesAsync(true, null, ct), "Normal application certificate check failed");
return app;
}
catch { await app.DisposeAsync(); throw; }
}
static bool ManagerDisposed(ICertificateManager manager)
{
Require(manager.GetType().FullName == "Opc.Ua.CertificateManager" && manager.GetType().Assembly.GetName().Version?.ToString() == "2.0.1.0", "Exact SDK diagnostic layout changed");
var field = manager.GetType().GetField("m_disposed", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
Require(field?.FieldType == typeof(bool), "Exact SDK disposal bool missing");
return (bool)field!.GetValue(manager)!;
}
static void PrivateFile(string path, byte[] bytes)
{
using var stream = new FileStream(path, new FileStreamOptions { Mode = FileMode.CreateNew,
Access = FileAccess.Write, UnixCreateMode = UnixFileMode.UserRead | UnixFileMode.UserWrite });
stream.Write(bytes);
}
public static async Task<int> Main(string[] args)
{
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(60));
ApplicationInstance? app = null;
try
{
Require(OperatingSystem.IsMacOS() && Environment.Version.ToString() == "10.0.14", "Expected matched macOS/.NET10.0.14 seed");
var root = Path.GetFullPath(args.Single());
Require(Directory.Exists(root) && (File.GetUnixFileMode(root) & (UnixFileMode)63) == 0, "Expected private fresh seed directory");
app = await Open(Path.Combine(root, "pki"), DefaultTelemetry.Create(_ => { }), deadline.Token);
var manager = app.ApplicationConfiguration!.CertificateManager;
string leaf;
using (var entry = manager.AcquireApplicationCertificateBySecurityPolicy(SecurityPolicies.Basic256Sha256))
{
Require(entry is not null && entry.Certificate.HasPrivateKey, "Expected SDK-generated private certificate");
var certificate = entry!.Certificate; leaf = Hash(certificate);
using var rsa = certificate.GetRSAPrivateKey(); using var publicKey = certificate.GetRSAPublicKey();
Require(rsa is not null && publicKey is not null, "Expected normal SDK RSA certificate");
var challenge = "native-import-seed-diagnostic"u8.ToArray();
var signature = rsa!.SignData(challenge, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Require(publicKey!.VerifyData(challenge, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1), "Seed key mismatch");
var privateKey = rsa.ExportRSAPrivateKey();
try { PrivateFile(Path.Combine(root, "leaf.der"), certificate.RawData); PrivateFile(Path.Combine(root, "private.pkcs1"), privateKey); }
finally { CryptographicOperations.ZeroMemory(privateKey); }
}
await app.DisposeAsync().AsTask().WaitAsync(deadline.Token); app = null;
Require(ManagerDisposed(manager) && Certificate.InstancesLeaked == 0, "Seed SDK ownership remains");
Console.WriteLine(JsonSerializer.Serialize(new { kind = "seed", leafSha256 = leaf, runtime = Environment.Version.ToString(),
sdkVersion = typeof(Certificate).Assembly.GetName().Version!.ToString(), managerDisposed = true,
certificateCoresOutstanding = Certificate.InstancesLeaked, keyFormat = "PKCS1-private-RSA", privateFileMode = "0600",
cryptographyRuntime = new { informationalVersion = typeof(X509Certificate2).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion,
sha256 = Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(typeof(X509Certificate2).Assembly.Location))).ToLowerInvariant() } }));
return 0;
}
catch(Exception error) { Console.WriteLine(JsonSerializer.Serialize(new {kind="failed", errorType=error.GetType().FullName, message=error.Message}));return 1; }
finally { if(app is not null) await app.DisposeAsync(); }
}
}
@@ -0,0 +1,64 @@
# Installed native bridge diagnostic — attempt A stopped at its ownership guard
The approved fixed-200 diagnostic did **not complete**. One measured native
child started; it stopped during its first operation because the harness required
an identity keychain association that the installed .NET wrapper allows to be
absent. This is a diagnostic model error. There is no repeated native retention
result, no production mitigation or resource-budget decision, and no change to
#799's status.
The separate SDK 2.0.1 seed completed with the expected RSA leaf/key pair,
observed application manager disposal, and private fixture files. The measured C
child loaded the pinned native bridge, checked its export image and rejected CLR
and SDK images. The C build succeeded with six unsuppressed deprecation warnings
for the exercised public keychain APIs; the seed build reported zero warnings
and errors. This was macOS **26.5.2** build **25F84** arm64; installed managed
cryptography was .NET **10.0.14**. [The inspected design](NATIVE_IMPORT_DESIGN.md)
records the exact native/managed binary paths and ABI.
| Observation | Recorded result |
|---|---|
| Before snapshot | AppleDatabase **1 / 768 shallow bytes**; Mach RSS **7,815,168 bytes**; 3 numeric FDs |
| DER import, modern RSA creation, private keychain create/lock, external representation and legacy ephemeral import | All reached bridge calls succeeded |
| Move to private keychain | Bridge return **1**, OSStatus **0**; post-move certificate keychain copy returned a verified private association |
| Post-move identity keychain copy | Bridge-normalized OSStatus **0**, **no returned keychain reference** |
| Stop | Before identity certificate copy; its uncalled status is the `INT32_MIN` sentinel, not a native OSStatus |
| Error-path cleanup | **9** owned CF outputs / **9** releases; logical owners **0**; verified private keychain native delete OSStatus **0**; no fallback cleanup |
| Process and fixture | Native exit **1**, reported elapsed **1.172310 s**; both builds, seed and native process confirmed reaped; no remaining owned keychain files before collector cleanup; private task files overwritten and unlinked after reaping; root removed |
Only four JSONL rows exist: before fingerprint, before checkpoint, partial
operation 1, failure. No first/repeated/idle snapshot or after fingerprint was
collected. The before-only default/search query succeeded; equality across the
attempt was **not measured**. The measured child made no setter or restore call.
No estimate of native heap growth can be drawn from its single before sample.
## Why the guard did not match the installed wrapper
`SafeTemporaryKeychainHandle.TrackItem` obtains a copied keychain and returns
without adding a logical owner when the copied handle is invalid. The installed
native bridge `AppleCryptoNative_SecKeychainItemCopyKeychain` at `0x8a04` normalizes
`errSecNoSuchKeychain` (`-25294`) and `errSecInvalidItemRef` (`-25304`) to success;
the receipt records the bridge result, not the underlying raw Apple status. The
installed move wrapper validates the move result and identity handle; it does
not require the identity's `TrackItem` to add an owner. Attempt A incorrectly
made that association mandatory before identity certificate copy.
Any separately reviewed follow-up should model ownership from actual validated
associations: keep all nine expected native copy calls and their statuses; add a
logical owner only when the returned keychain matches the generated private
reference/path; record no association when it is null. Reject a disappearing
association during Untrack of a previously tracked owner. Record the actual
zero-owner deletion stage and which item/copy references remain alive, rather
than requiring deletion at identity Untrack. A single partial attempt cannot
predict the copied certificate's association or a fixed number of non-null
copies. This correction concerns the diagnostic only, not production SDK
ownership or certificate selection.
Selected receipts and verified raw/projection hashes are in
[`evidence/2026-11-10-native-import-attempt-a`](evidence/2026-11-10-native-import-attempt-a/verification.json).
The unchanged raw directory is `/tmp/opcua2-native-import-20261110a`. It contains
raw native snapshots but no retained private fixture. The native executable
SHA-256 is `e5c5caf2a8f882f82b738396156b378fd157ebd1a825d462449cd2a0497830fd`;
the seed DLL SHA-256 is
`1fa7de542aee11acab18dcc8d4096b21fcbc16e4edc1fca2243159dd1176adc0`.
This was a single serialized execution. No retry or counterfactual arm ran.
@@ -0,0 +1,179 @@
# Native bridge import control — inspected design
Status: Astra approved the refined ABI/ownership design for implementation. The
C child, separate seed and collector are implemented and submitted for actual-code
review. The original assumptions below are retained to explain the exact attempt.
[Attempt A](NATIVE_IMPORT_ATTEMPT.md) stopped at its first partial operation: the
installed wrapper permits null item associations, contrary to the original fixed
identity-association expectation. It produced no repeated retention result. The coordinator confirmed a
single staged native-bridge control; a handwritten Apple import reproduction is
deferred until the result shows whether it is useful. This is not a production
routing/crypto change, a mitigation or an acceptance decision.
## Installed binaries and retained inspection
Installed managed crypto:
`/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App/10.0.14/System.Security.Cryptography.dll`.
Informational version `10.0.14+0427b9abbead3fa64c6655eb4290c537397131fa`;
SHA-256 `f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a`.
Installed native bridge:
`/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App/10.0.14/libSystem.Security.Cryptography.Native.Apple.dylib`.
SHA-256 `0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c`.
Only this exact, unmodified dylib would be loaded by absolute path; required
exports and actual loaded-image identity must match. No SDK/CLR may be loaded in
the measured C child. Its native bridge dependency is explicitly part of this
experiment, unlike the preceding plain-Apple negative control.
Retained actual-binary inspection files (read-only ILSpy 10.1.0.8386 / otool):
| File | Relevant locations |
|---|---|
| `/tmp/opcua2-native-runtime-loader-decompiled.cs` | 836–870 load certs/keys/match, initialize import state and FromCertAndKey; 1555–1581 exportable branch; 1611–1640 keychain initialization and GetPrivateKey |
| `/tmp/opcua2-native-runtime-pkcs12key-decompiled.cs` | AsymmetricAlgorithmPkcs12PrivateKey imports PKCS8 into its RSA algorithm |
| `/tmp/opcua2-native-runtime-pkcs12key-base-decompiled.cs` | Dispose calls the algorithm's Dispose |
| `/tmp/opcua2-native-runtime-rsa-decompiled.cs` | 95–108 ImportParameters/CreateDataKey/CopyPublicKey; 431–458 PKCS1 serialization to CreateDataKey; export uses native external representation |
| `/tmp/opcua2-native-runtime-applepal-decompiled.cs` | 339–349 identity constructor/certificate copy and disposal; 555–564 MoveToKeychain |
| `/tmp/opcua2-native-runtime-appleinterop-decompiled.cs` | PAL RSA enum 544–549; CreateDataKey/external representation/public copy 1982–2127; keychain create/never-lock 3054/3209/3481; legacy key import 3563/3596; cert import 3697/3944; move 3878/4036; identity cert copy 2629/4087 |
| `/tmp/opcua2-native-runtime-certhandle-decompiled.cs` and `identityhandle-decompiled.cs` | Both actual types inherit SafeKeychainItemHandle with no release override |
| `/tmp/opcua2-native-runtime-keychainitem-decompiled.cs` | SafeKeychainItemHandle release: UntrackItem copy/release, then CFRelease; certificate and identity inherit this class |
| `/tmp/opcua2-native-runtime-keypair-decompiled.cs` | Actual SecKeyPair Dispose: modern private-key owner before modern public-key owner |
| `/tmp/opcua2-native-runtime-keyhandle-decompiled.cs` | SafeSecKeyRefHandle release: straight CFRelease, then optional managed parent release; no tracking copy |
| `/tmp/opcua2-native-runtime-keychain-decompiled.cs` | TrackItem/UntrackItem each copy/release a keychain; matching dictionary only adds/releases managed SafeHandle ownership |
| `/tmp/opcua2-native-runtime-x509contenttype-decompiled.cs` | Actual public Cert enum value 1; Pfx/Pkcs12 value 3 |
| `/tmp/opcua2-native-runtime-apple-disassembly.txt` | `_AppleCryptoNative_SecKeyImportEphemeral`, `_AddKeyToKeychain`, `_AppleCryptoNative_X509MoveToKeychain`, all required exports |
The installed native call sites, not merely the public runtime tag, show:
- `SecKeyImportEphemeral` at `0x92e8` calls `SecItemImport` at `0x9390`,
followed by CF ownership cleanup.
- `AddKeyToKeychain` calls `SecItemExport` at `0xa0a8` and `SecItemImport` at
`0xa11c`, then retains an optional returned key and releases local containers.
- `X509MoveToKeychain` at `0xa180` calls AddKeyToKeychain at `0xa240`,
`SecCertificateAddToKeychain` at `0xa278`, and a private-search-list identity
`SecItemCopyMatching` at `0xa388`, with CF cleanup and one returned identity.
Public `v10.0.14` native headers/source are explanatory cross-checks, not asserted
as the exact installed source commit. In particular this **Exportable** path is
managed PKCS12 decode / RSA materialization → legacy key import → move-to-keychain.
A guessed `SecPKCS12Import` control would exercise a different path.
## ABI and returned ownership map
All bridge integer return values below are signed 32-bit. The installed generated
managed keychain-create declaration takes signed `int utf8PassphraseLength`;
the explanatory public native header takes uint32. The control uses the same
fixed positive bounded value 129, whose ABI representation is identical. Other
byte lengths are signed 32-bit. Native
pointers are opaque CF references and must never be printed. Every out location
starts null/zero; each non-null owned output must be released once on success or
failure. Borrowed CFData byte pointers are valid only while their owned CFData is
held and are never separately freed. CFError is an owned output, recorded only
by code and released once, without description/key logging.
| Exact export | Argument/return contract |
|---|---|
| `AppleCryptoNative_X509ImportCertificate` | `(uint8_t*, int32 len, int32 contentType=1, CFStringRef NULL, SecKeychainRef NULL, int32 exportable=1, SecCertificateRef*, SecIdentityRef*, int32 OSStatus*)`; bridge return 1 and OSStatus 0 required; DER must yield an owned cert and no identity |
| `AppleCryptoNative_SecKeyCreateWithData` | `(uint8_t*, int32 len, uint32 PAL_RSA=2, int32 isPublic=0, SecKeyRef*, CFErrorRef*)`; bridge return 1, owned modern RSA key |
| `AppleCryptoNative_SecKeyCopyPublicKey` | `(SecKeyRef) -> SecKeyRef`; owned public-key copy, non-null required |
| `AppleCryptoNative_SecKeyCopyExternalRepresentation` | `(SecKeyRef, CFDataRef*, CFErrorRef*)`; bridge return 1, owned PKCS1 private-key data, no key bytes logged |
| `AppleCryptoNative_SecKeychainCreate` | `(const char*, int32 passwordLength=129 in installed managed ABI, const uint8_t*, SecKeychainRef*)`; OSStatus 0, owned fresh private keychain; same native false prompt flag |
| `AppleCryptoNative_SetKeychainNeverLock` | `(SecKeychainRef) -> OSStatus`; 0 required; affects only the generated private keychain, matching installed import state |
| `AppleCryptoNative_SecKeyImportEphemeral` | `(uint8_t*, int32 len, int32 isPrivate=1, SecKeyRef*, int32 OSStatus*)`; bridge return 1 / OSStatus 0, owned legacy private key |
| `AppleCryptoNative_X509MoveToKeychain` | `(SecCertificateRef, SecKeychainRef, SecKeyRef, SecIdentityRef*, int32 OSStatus*)`; bridge return 1 / OSStatus 0 and owned non-null identity required; native helper scopes its identity query to target keychain and supplied cert |
| `AppleCryptoNative_X509CopyCertFromIdentity` | `(SecIdentityRef, SecCertificateRef*) -> OSStatus`; 0 required, owned certificate copy |
| `AppleCryptoNative_SecKeychainItemCopyKeychain` | `(CFTypeRef item, SecKeychainRef*) -> int32`; bridge-normalized OSStatus 0 required; non-null returned keychain is owned and CFReleased once. The installed bridge normalizes native -25304/-25294 to 0; null output is valid for an unassociated item. Preserve normalized status and output-present flag, not raw underlying status. |
| `AppleCryptoNative_SecKeychainDelete` | `(SecKeychainRef) -> OSStatus`; recorded separately from subsequent CFRelease; only a returned keychain verified against this generated private path may be deleted |
## Matching native interactions from managed wrapper tracking
The child will include the bridge-normalized `SecKeychainItemCopyKeychain` calls
made by installed wrappers, not merely the primary import/move exports. Every
non-null copied keychain is released once. The map is:
1. After DER import, TrackItem(cert): copy/release its associated keychain.
The null identity output is skipped, as the managed TrackItem does.
2. Around MoveToKeychain: acquire the outer cert-keychain copy and keep it alive
across the entire move wrapper; separately copy/release cert's old keychain
for UntrackItem; call Move; copy/release cert's new keychain and identity's
keychain for the two TrackItem calls; then release the outer copy.
3. After identity certificate copy: copy/release copied cert's keychain for
TrackItem.
4. Releasing initial cert, copied cert and identity: copy/release each item's
keychain first (UntrackItem), then CFRelease the item. This preserves the
installed PAL order: initial cert after the replacement PAL is constructed,
then replacement cert before identity at disposal.
5. Modern public/private and legacy private `SafeSecKeyRefHandle` release maps
directly to CFRelease, with no keychain tracking native call. Its optional
managed parent lifetime is not used by the inspected GetPrivateKey path.
No managed dictionary is recreated. A small logical owner counter preserves the
known deterministic ownership/deletion order. It starts at one import-state
owner. Successful TrackItem of an item associated with the generated keychain
adds one; UntrackItem subtracts one. These logical increments are not CF retains.
After the replacement PAL is constructed and the original cert is disposed,
ImportState.Dispose relinquishes its initial logical ownership. The counter then
contains only copied-cert and identity owners. Their Untrack calls subtract one
before releasing each copied-keychain reference. The final identity Untrack
reaches zero: it calls SecKeychainDelete and releases the initial native keychain
owner **while the copied keychain ref and identity are still alive**, then
releases the copied keychain ref, then CFReleases the identity. This matches the
installed SafeHandle delayed-release ordering; deliberately postponing deletion
to after all item CFReleases would be a different reduced sequence. The legacy
private key is released explicitly after this sequence (the managed helper lacks
an explicit local using scope); exact managed finalizer timing is not emulated.
The installed bridge at `0x8a04` checks item type, calls the public Apple copy API,
and normalizes -25304/-25294 to success. The control calls that pinned export
rather than interpreting a raw public return differently. Any other returned
status fails the operation but is retained in the receipt.
## Proposed one-child sequence and boundaries
Prepare one fresh normal SDK-generated RSA fixture in a **separate seed process**,
not in the measured child. Export its public DER certificate and RSA PKCS1 private
key through public APIs into a private directory with files mode 0600. Preserve
only public identity/source/binary hashes, never private bytes/passwords. The seed
and measured child must be reaped before their key material is overwritten and
unlinked; cleanup is confined to their fresh owned roots. No existing PKI is used.
One plain C child would then repeat exactly 200 times, without count/retry
extension:
1. Import public DER through the pinned bridge. Materialize the modern RSA key
from PKCS1 through CreateWithData and obtain the owned public-key copy, matching
the inspected RSA loader prelude without reproducing managed ASN1/PFX matching.
2. Create one generated unrelated private keychain path and set its never-lock
behavior through the pinned bridge. Use generated password bytes and no prompt;
verify its returned path is exactly the generated private filename/backing file.
3. Export modern private-key representation, import the legacy ephemeral key,
then release exported CFData and modern RSA private/public owners in that order, as the
managed Pkcs12Key is disposed before MoveToKeychain.
4. Move certificate/key to that private keychain; require a typed identity.
Copy its certificate as the AppleCertificatePal identity constructor does;
release the original certificate. At first/last only, check returned public
certificate identity against the input; no secret/private-key logging.
5. Relinquish the initial import-state logical owner after original cert
disposal. Release copied cert (Untrack/copy-release then cert CFRelease), then
identity. Its final Untrack deletes/releases the verified initial keychain
owner before releasing that Untrack copy and identity, as mapped above.
Release the explicitly held legacy key. Preserve every bridge
return, OSStatus, CFError code, owned-output/release count and failure path.
The modern/legacy distinction is explicit. The installed managed helper's local
legacy SafeSecKeyRef has no explicit using scope in FromCertAndKey; the native
control uses balanced explicit CF owners and does **not** claim that this is a
proven GC bug, or exactly emulate managed finalizer timing.
Use the already accepted before/first/200/five-second-idle checkpoints, raw native
heap inventory, Mach RSS, total/owned-keychain FDs and backing-file counts, and
read-only successful default/search-list fingerprint equality. No list setter or
restore call. Keep fixed parent/process/probe/reap bounds, complete JSONL parsing,
private generated paths, fail-closed ABI/hash/image checks and child reaping.
A positive result would show that managed caller participation is not necessary
for this observed import-path retention, while retaining the exact installed
native bridge code. It would **not** distinguish a bridge-internal native owner
from Apple caching; a later independently owned public-Apple sequence could be
justified then. A flat result would not by itself prove a managed bug: omitted
managed parsing/matching, registry bookkeeping or lifetime differences remain. No
production implementation or additional experiment is approved by this design.
@@ -0,0 +1,264 @@
// Diagnostic only: pinned installed native bridge import; no measured managed caller or PFX parser.
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
#include <CommonCrypto/CommonDigest.h>
#include <mach/mach.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REPETITIONS 200
static char root[PATH_MAX];
static struct timespec started;
static unsigned created_refs, released_refs, query_releases;
static double elapsed(void) {
struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now);
return (double)(now.tv_sec-started.tv_sec)+(double)(now.tv_nsec-started.tv_nsec)/1e9;
}
static void die(const char *message) {
fprintf(stdout,"{\"kind\":\"failed\",\"message\":\"%s\",\"createdRefs\":%u,\"releasedRefs\":%u,\"elapsedSeconds\":%.6f}\n",message,created_refs,released_refs,elapsed());
fflush(stdout); exit(1);
}
static void hex(const unsigned char *bytes, size_t length, char *out) {
const char *alphabet="0123456789abcdef";
for(size_t i=0;i<length;i++){out[i*2]=alphabet[bytes[i]>>4];out[i*2+1]=alphabet[bytes[i]&15];}
out[length*2]=0;
}
static void wipe(void *memory, size_t length) {
volatile unsigned char *bytes=(volatile unsigned char *)memory;
while(length--)*bytes++=0;
}
static OSStatus keychain_path(SecKeychainRef keychain, char *path) {
if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return errSecParam;
UInt32 length=PATH_MAX-1;
OSStatus status=SecKeychainGetPath(keychain,&length,path);
if(status==errSecSuccess && length<PATH_MAX)path[length]=0;
return status;
}
struct fingerprint { char default_sha[65], search_sha[65]; CFIndex search_count; };
static struct fingerprint fingerprint(const char *stage) {
SecKeychainRef default_keychain=NULL;
CFArrayRef search=NULL;
struct fingerprint out; memset(&out,0,sizeof(out));
char path[PATH_MAX]; memset(path,0,sizeof(path));
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
OSStatus default_status=SecKeychainCopyDefault(&default_keychain);
OSStatus default_path_status=default_keychain?keychain_path(default_keychain,path):errSecInvalidKeychain;
if(default_status==errSecSuccess && default_path_status==errSecSuccess){CC_SHA256(path,(CC_LONG)strlen(path),digest);hex(digest,sizeof(digest),out.default_sha);}
if(default_keychain){CFRelease(default_keychain);query_releases++;default_keychain=NULL;}
OSStatus search_status=SecKeychainCopySearchList(&search);
OSStatus search_path_status=errSecSuccess;
CC_SHA256_CTX hash; CC_SHA256_Init(&hash);
if(!search||CFGetTypeID(search)!=CFArrayGetTypeID())search_path_status=errSecParam;
if(search_status==errSecSuccess && search && search_path_status==errSecSuccess){
out.search_count=CFArrayGetCount(search);
uint64_t count=(uint64_t)out.search_count; CC_SHA256_Update(&hash,&count,sizeof(count));
for(CFIndex i=0;i<out.search_count;i++){
// CFArray values are borrowed. Releasing the owned array balances its element references.
SecKeychainRef borrowed=(SecKeychainRef)CFArrayGetValueAtIndex(search,i);
memset(path,0,sizeof(path)); OSStatus status=keychain_path(borrowed,path);
if(status!=errSecSuccess){search_path_status=status;break;}
uint32_t length=(uint32_t)strlen(path);CC_SHA256_Update(&hash,&length,sizeof(length));CC_SHA256_Update(&hash,path,length);
}
}
CC_SHA256_Final(digest,&hash);hex(digest,sizeof(digest),out.search_sha);
if(search){CFRelease(search);query_releases++;search=NULL;}
fprintf(stdout,"{\"kind\":\"fingerprint\",\"stage\":\"%s\",\"defaultStatus\":%d,\"defaultPathStatus\":%d,\"searchStatus\":%d,\"searchPathStatus\":%d,\"defaultSha256\":\"%s\",\"searchSha256\":\"%s\",\"searchCount\":%ld,\"ownedQueryReferencesReleased\":%u}\n",stage,(int)default_status,(int)default_path_status,(int)search_status,(int)search_path_status,out.default_sha,out.search_sha,(long)out.search_count,query_releases);fflush(stdout);
if(default_status || default_path_status || search_status || search_path_status)die("Read-only keychain fingerprint failed");
return out;
}
static void checkpoint(const char *stage, unsigned operations) {
struct mach_task_basic_info info; mach_msg_type_number_t count=MACH_TASK_BASIC_INFO_COUNT;
kern_return_t status=task_info(mach_task_self(),MACH_TASK_BASIC_INFO,(task_info_t)&info,&count);
if(status!=KERN_SUCCESS)die("task_info failed");
struct timespec wall;clock_gettime(CLOCK_REALTIME,&wall);struct tm utc;gmtime_r(&wall.tv_sec,&utc);
char stamp[40];strftime(stamp,sizeof(stamp),"%Y-%m-%dT%H:%M:%S",&utc);
fprintf(stdout,"{\"kind\":\"checkpoint\",\"mode\":\"native-bridge-import\",\"stage\":\"%s\",\"operations\":%u,\"processId\":%d,\"utc\":\"%s.%03ldZ\",\"elapsedSeconds\":%.6f,\"rssBytes\":%llu,\"createdRefs\":%u,\"releasedRefs\":%u,\"managedRuntimePresent\":false}\n",stage,operations,getpid(),stamp,wall.tv_nsec/1000000,elapsed(),(unsigned long long)info.resident_size,created_refs,released_refs);fflush(stdout);
char next[PATH_MAX];int n=snprintf(next,sizeof(next),"%s/continue-%s",root,stage);if(n<0 || (size_t)n>=sizeof(next))die("Checkpoint path overflow");
while(access(next,F_OK)!=0){if(elapsed()>180)die("Checkpoint deadline exceeded");struct timespec pause={0,25000000};nanosleep(&pause,NULL);}
}
static int absent(const char *path){struct stat state;if(lstat(path,&state)==0)return 0;return errno==ENOENT;}
#define BRIDGE_SHA "0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c"
static int32_t (*import_cert)(const uint8_t*,int32_t,int32_t,CFStringRef,SecKeychainRef,int32_t,SecCertificateRef*,SecIdentityRef*,int32_t*);
static int32_t (*create_key)(const uint8_t*,int32_t,uint32_t,int32_t,SecKeyRef*,CFErrorRef*);
static SecKeyRef (*copy_public)(SecKeyRef);
static int32_t (*external_key)(SecKeyRef,CFDataRef*,CFErrorRef*);
static int32_t (*create_chain)(const char*,int32_t,const uint8_t*,SecKeychainRef*);
static int32_t (*never_lock)(SecKeychainRef);
static int32_t (*import_key)(const uint8_t*,int32_t,int32_t,SecKeyRef*,int32_t*);
static int32_t (*move_key)(SecCertificateRef,SecKeychainRef,SecKeyRef,SecIdentityRef*,int32_t*);
static int32_t (*copy_cert)(SecIdentityRef,SecCertificateRef*);
static int32_t (*copy_chain)(CFTypeRef,SecKeychainRef*);
static int32_t (*delete_chain)(SecKeychainRef);
static unsigned all_created,all_released;
static void owned(CFTypeRef value){if(value)all_created++;}
static void drop(CFTypeRef *value){if(*value){CFRelease(*value);*value=NULL;all_released++;}}
#define DROP(value) drop((CFTypeRef*)&(value))
static void image_guard(void){
unsigned bridge_images=0;
for(uint32_t i=0;i<_dyld_image_count();i++){
const char *name=_dyld_get_image_name(i);
if(strstr(name,"libcoreclr")||strstr(name,"libhostfxr")||strstr(name,"Opc.Ua"))die("Measured child loaded a managed runtime or SDK");
if(strstr(name,"libSystem.Security.Cryptography.Native.Apple.dylib"))bridge_images++;
}
if(bridge_images!=1)die("Expected exactly one pinned native bridge image");
}
static void load_bridge(const char *path){
FILE *file=fopen(path,"rb");if(!file)die("Pinned bridge could not be read");
CC_SHA256_CTX hash;CC_SHA256_Init(&hash);unsigned char bytes[8192],digest[32];size_t size;
while((size=fread(bytes,1,sizeof(bytes),file)))CC_SHA256_Update(&hash,bytes,(CC_LONG)size);
int bad=ferror(file);fclose(file);if(bad)die("Pinned bridge read failed");CC_SHA256_Final(digest,&hash);
char actual[65];hex(digest,sizeof(digest),actual);if(strcmp(actual,BRIDGE_SHA))die("Pinned bridge hash changed");
void *library=dlopen(path,RTLD_NOW|RTLD_LOCAL);if(!library)die("Pinned bridge load failed");
#define LOAD(variable,symbol) do { *(void**)(&variable)=dlsym(library,symbol); if(!variable)die("Pinned bridge export missing"); Dl_info info;char loaded[PATH_MAX],expected[PATH_MAX];if(!dladdr((void*)variable,&info)||!realpath(info.dli_fname,loaded)||!realpath(path,expected)||strcmp(loaded,expected))die("Bridge export image identity changed"); } while(0)
LOAD(import_cert,"AppleCryptoNative_X509ImportCertificate");LOAD(create_key,"AppleCryptoNative_SecKeyCreateWithData");
LOAD(copy_public,"AppleCryptoNative_SecKeyCopyPublicKey");LOAD(external_key,"AppleCryptoNative_SecKeyCopyExternalRepresentation");
LOAD(create_chain,"AppleCryptoNative_SecKeychainCreate");LOAD(never_lock,"AppleCryptoNative_SetKeychainNeverLock");
LOAD(import_key,"AppleCryptoNative_SecKeyImportEphemeral");LOAD(move_key,"AppleCryptoNative_X509MoveToKeychain");
LOAD(copy_cert,"AppleCryptoNative_X509CopyCertFromIdentity");LOAD(copy_chain,"AppleCryptoNative_SecKeychainItemCopyKeychain");
LOAD(delete_chain,"AppleCryptoNative_SecKeychainDelete");
#undef LOAD
image_guard();
}
struct copy_result {const char *stage;int32_t status;int present,associated;};
struct operation_state {
SecCertificateRef cert,copied;SecIdentityRef identity;SecKeyRef modern,public_key,legacy;
CFDataRef external;SecKeychainRef keychain,keychain_identity,outer;CFErrorRef error;
char path[PATH_MAX],backing[PATH_MAX];
unsigned logical_owners;int initial_owner,cert_tracked,copied_tracked,identity_tracked;
int failed,path_verified,deleted,delete_during_identity_untrack,fallback_cleanup;
int32_t cert_return,cert_status,modern_return,create_status,lock_status,external_return,key_return,key_status,move_return,move_status,copy_cert_status,delete_status,path_status;
CFIndex modern_error,external_error;int modern_error_present,external_error_present;unsigned copy_count;
struct copy_result copies[16];
};
static int private_path(struct operation_state *s,SecKeychainRef keychain){
if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return 0;
char actual[PATH_MAX];memset(actual,0,sizeof(actual));int32_t status=keychain_path(keychain,actual);
return status==0 && (!strcmp(actual,s->path)||!strcmp(actual,s->backing));
}
static void delete_owner(struct operation_state *s,const char *stage){
if(!s->keychain)return;
if(s->deleted){s->failed=1;return;}
s->path_verified=private_path(s,s->keychain);
if(s->path_verified){s->delete_status=delete_chain(s->keychain);s->deleted=1;}
if(!s->path_verified||s->delete_status)s->failed=1;
s->delete_during_identity_untrack=!strcmp(stage,"release-identity")&&s->identity!=NULL&&s->copied==NULL;
DROP(s->keychain);released_refs++;
}
static void relinquish(struct operation_state *s,const char *stage){
if(!s->logical_owners){s->failed=1;return;}
if(--s->logical_owners==0)delete_owner(s,stage);
}
static SecKeychainRef copied_chain(struct operation_state *s,CFTypeRef item,const char *stage,int *associated){
*associated=0;if(!item)return NULL;
CFTypeID type=CFGetTypeID(item);if(type!=SecCertificateGetTypeID()&&type!=SecIdentityGetTypeID()){s->failed=1;return NULL;}
SecKeychainRef result=NULL;int32_t status=copy_chain(item,&result);owned(result);
int type_ok=!result||CFGetTypeID(result)==SecKeychainGetTypeID();
int belongs=result&&type_ok&&s->keychain_identity==result&&private_path(s,result);
if(s->copy_count<16)s->copies[s->copy_count++]=(struct copy_result){stage,status,result!=NULL,belongs};else s->failed=1;
if(status || !type_ok || (result&&!belongs))s->failed=1;
*associated=status==0&&belongs;return result;
}
static void track(struct operation_state *s,CFTypeRef item,const char *stage,int *tracked){
int associated=0;SecKeychainRef copy=copied_chain(s,item,stage,&associated);
if(associated){if(*tracked||!s->keychain)s->failed=1;else{s->logical_owners++;*tracked=1;}}
DROP(copy);
}
static void untrack(struct operation_state *s,CFTypeRef item,const char *stage,int *tracked){
if(!item)return;
int associated=0;SecKeychainRef copy=copied_chain(s,item,stage,&associated);
if(*tracked){if(associated){*tracked=0;relinquish(s,stage);}else s->failed=1;}
else if(associated)s->failed=1;
// Final logical-owner release above precedes this copy's CFRelease.
DROP(copy);
}
static int leaf_equal(SecCertificateRef cert,const unsigned char *der,size_t length){
CFDataRef data=SecCertificateCopyData(cert);owned(data);
int equal=data&&CFGetTypeID(data)==CFDataGetTypeID()&&CFDataGetLength(data)==(CFIndex)length&&!memcmp(CFDataGetBytePtr(data),der,length);
DROP(data);return equal;
}
static void import_operation(unsigned iteration,const unsigned char *der,size_t der_length,const unsigned char *key,size_t key_length){
struct operation_state s;memset(&s,0,sizeof(s));
s.cert_return=s.cert_status=s.modern_return=s.create_status=s.lock_status=s.external_return=s.key_return=s.key_status=s.move_return=s.move_status=s.copy_cert_status=s.delete_status=s.path_status=INT32_MIN;
unsigned before_created=all_created,before_released=all_released;
unsigned char random[144],password[129];char suffix[33];
if(SecRandomCopyBytes(kSecRandomDefault,sizeof(random),random))die("Secure random failed");
hex(random,16,suffix);const unsigned char choices[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@";
for(unsigned i=0;i<128;i++)password[i]=choices[random[16+i]&63];password[128]=0;wipe(random,sizeof(random));
int n=snprintf(s.path,sizeof(s.path),"%s/tmp/native-import-%s.keychain",root,suffix);
if(n<0||(size_t)n>=sizeof(s.path))die("Private fixture path overflow");
n=snprintf(s.backing,sizeof(s.backing),"%s-db",s.path);
if(n<0||(size_t)n>=sizeof(s.backing)||!absent(s.path)||!absent(s.backing))die("Private fixture path already exists or overflows");
s.cert_return=import_cert(der,(int32_t)der_length,1,NULL,NULL,1,&s.cert,&s.identity,&s.cert_status);owned(s.cert);owned(s.identity);
track(&s,s.cert,"import-cert-track",&s.cert_tracked);
if(s.identity)track(&s,s.identity,"unexpected-import-identity-track",&s.identity_tracked);
if(s.cert_return!=1||s.cert_status||!s.cert||CFGetTypeID(s.cert)!=SecCertificateGetTypeID()||s.identity||s.failed){s.failed=1;goto cleanup;}
s.modern_return=create_key(key,(int32_t)key_length,2,0,&s.modern,&s.error);owned(s.modern);owned(s.error);
s.modern_error_present=s.error!=NULL;if(s.error){if(CFGetTypeID(s.error)==CFErrorGetTypeID())s.modern_error=CFErrorGetCode(s.error);else s.failed=1;}DROP(s.error);
if(s.modern_return!=1||!s.modern||CFGetTypeID(s.modern)!=SecKeyGetTypeID()||s.modern_error_present||s.failed){s.failed=1;goto cleanup;}
s.public_key=copy_public(s.modern);owned(s.public_key);if(!s.public_key||CFGetTypeID(s.public_key)!=SecKeyGetTypeID()){s.failed=1;goto cleanup;}
s.create_status=create_chain(s.path,129,password,&s.keychain);owned(s.keychain);wipe(password,sizeof(password));
if(s.keychain){created_refs++;s.keychain_identity=s.keychain;s.initial_owner=1;s.logical_owners=1;char actual[PATH_MAX];s.path_status=keychain_path(s.keychain,actual);s.path_verified=private_path(&s,s.keychain);}
if(s.create_status||!s.keychain||s.path_status||!s.path_verified){s.failed=1;goto cleanup;}
s.lock_status=never_lock(s.keychain);if(s.lock_status){s.failed=1;goto cleanup;}
s.external_return=external_key(s.modern,&s.external,&s.error);owned(s.external);owned(s.error);
s.external_error_present=s.error!=NULL;if(s.error){if(CFGetTypeID(s.error)==CFErrorGetTypeID())s.external_error=CFErrorGetCode(s.error);else s.failed=1;}DROP(s.error);
if(s.external_return!=1||!s.external||CFGetTypeID(s.external)!=CFDataGetTypeID()||s.external_error_present||s.failed||CFDataGetLength(s.external)<=0||CFDataGetLength(s.external)>65536){s.failed=1;goto cleanup;}
s.key_return=import_key(CFDataGetBytePtr(s.external),(int32_t)CFDataGetLength(s.external),1,&s.legacy,&s.key_status);owned(s.legacy);
DROP(s.external);DROP(s.modern);DROP(s.public_key); // Installed SecKeyPair releases private before public.
if(s.key_return!=1||s.key_status||!s.legacy||CFGetTypeID(s.legacy)!=SecKeyGetTypeID()){s.failed=1;goto cleanup;}
int associated=0;s.outer=copied_chain(&s,s.cert,"move-outer-copy",&associated);
untrack(&s,s.cert,"move-cert-untrack",&s.cert_tracked);
if(s.failed)goto cleanup;
s.move_return=move_key(s.cert,s.keychain,s.legacy,&s.identity,&s.move_status);owned(s.identity);
track(&s,s.cert,"move-cert-track",&s.cert_tracked);track(&s,s.identity,"move-identity-track",&s.identity_tracked);DROP(s.outer);
if(s.move_return!=1||s.move_status||!s.identity||CFGetTypeID(s.identity)!=SecIdentityGetTypeID()||!s.cert_tracked||!s.identity_tracked||s.failed){s.failed=1;goto cleanup;}
s.copy_cert_status=copy_cert(s.identity,&s.copied);owned(s.copied);track(&s,s.copied,"identity-cert-track",&s.copied_tracked);
if(s.copy_cert_status||!s.copied||CFGetTypeID(s.copied)!=SecCertificateGetTypeID()||!s.copied_tracked||s.failed){s.failed=1;goto cleanup;}
if((iteration==1||iteration==REPETITIONS)&&!leaf_equal(s.copied,der,der_length)){s.failed=1;goto cleanup;}
cleanup:
wipe(password,sizeof(password));DROP(s.error);DROP(s.external);DROP(s.modern);DROP(s.public_key);DROP(s.outer);
untrack(&s,s.cert,"release-original-cert",&s.cert_tracked);DROP(s.cert);
if(s.initial_owner){s.initial_owner=0;relinquish(&s,"release-import-state");}
untrack(&s,s.copied,"release-copied-cert",&s.copied_tracked);DROP(s.copied);
untrack(&s,s.identity,"release-identity",&s.identity_tracked);DROP(s.identity);
DROP(s.legacy); // Explicit lifetime; managed helper does not scope this local with using.
if(s.keychain){s.failed=1;s.fallback_cleanup=1;delete_owner(&s,"failure-fallback-after-items");}
unsigned made=all_created-before_created,released=all_released-before_released;
if(made!=released||s.logical_owners||s.cert_tracked||s.copied_tracked||s.identity_tracked)s.failed=1;
if(!s.failed&&(s.copy_count!=9||!s.delete_during_identity_untrack||!s.deleted))s.failed=1;
fprintf(stdout,"{\"kind\":\"operation\",\"iteration\":%u,\"certReturn\":%d,\"certStatus\":%d,\"modernReturn\":%d,\"modernErrorCode\":%ld,\"modernErrorPresent\":%s,\"createStatus\":%d,\"pathStatus\":%d,\"neverLockStatus\":%d,\"externalReturn\":%d,\"externalErrorCode\":%ld,\"externalErrorPresent\":%s,\"legacyReturn\":%d,\"legacyStatus\":%d,\"moveReturn\":%d,\"moveStatus\":%d,\"copyCertStatus\":%d,\"deleteStatus\":%d,\"privatePathVerified\":%s,\"deleteDuringFinalIdentityUntrack\":%s,\"fallbackCleanup\":%s,\"logicalOwnersRemaining\":%u,\"ownedOutputs\":%u,\"releasedOutputs\":%u,\"failed\":%s,\"copies\":[",iteration,s.cert_return,s.cert_status,s.modern_return,(long)s.modern_error,s.modern_error_present?"true":"false",s.create_status,s.path_status,s.lock_status,s.external_return,(long)s.external_error,s.external_error_present?"true":"false",s.key_return,s.key_status,s.move_return,s.move_status,s.copy_cert_status,s.delete_status,s.path_verified?"true":"false",s.delete_during_identity_untrack?"true":"false",s.fallback_cleanup?"true":"false",s.logical_owners,made,released,s.failed?"true":"false");
for(unsigned i=0;i<s.copy_count;i++){struct copy_result c=s.copies[i];fprintf(stdout,"%s{\"stage\":\"%s\",\"status\":%d,\"returnedRef\":%s,\"privateAssociation\":%s}",i?",":"",c.stage,c.status,c.present?"true":"false",c.associated?"true":"false");}
fprintf(stdout,"]}\n");fflush(stdout);if(s.failed)die("Native bridge import operation failed after ownership cleanup");
}
static unsigned char *read_fixture(const char *name,size_t *length){
char path[PATH_MAX];snprintf(path,sizeof(path),"%s/seed/%s",root,name);struct stat state;
if(lstat(path,&state)||!S_ISREG(state.st_mode)||(state.st_mode&077)||state.st_nlink!=1||state.st_size<1||state.st_size>65536)die("Expected private bounded regular fixture");
FILE *file=fopen(path,"rb");if(!file)die("Fixture read failed");*length=(size_t)state.st_size;
unsigned char *bytes=malloc(*length);if(!bytes){fclose(file);die("Fixture allocation failed");}
int bad=fread(bytes,1,*length,file)!=*length||ferror(file);fclose(file);if(bad){wipe(bytes,*length);free(bytes);die("Fixture read incomplete");}return bytes;
}
int main(int argc,char **argv){
clock_gettime(CLOCK_MONOTONIC,&started);
if(argc!=3||!realpath(argv[1],root))die("Expected private fixture root and pinned bridge path");
struct stat state;if(stat(root,&state)||!S_ISDIR(state.st_mode)||(state.st_mode&077))die("Fixture root must be mode0700");
load_bridge(argv[2]);size_t der_length,key_length;
unsigned char *der=read_fixture("leaf.der",&der_length),*key=read_fixture("private.pkcs1",&key_length);
struct fingerprint before=fingerprint("before");checkpoint("before",0);
for(unsigned i=1;i<=REPETITIONS;i++){import_operation(i,der,der_length,key,key_length);if(i==1)checkpoint("first",i);}
checkpoint("repeated",REPETITIONS);struct timespec idle={5,0};while(nanosleep(&idle,&idle)){if(errno!=EINTR)die("Idle delay failed");}
struct fingerprint after=fingerprint("after");image_guard();checkpoint("idle",REPETITIONS);
wipe(key,key_length);free(key);free(der);
if(strcmp(before.default_sha,after.default_sha)||strcmp(before.search_sha,after.search_sha)||before.search_count!=after.search_count||created_refs!=REPETITIONS||released_refs!=REPETITIONS||all_created!=all_released)die("Fingerprint or ownership guard failed");
fprintf(stdout,"{\"kind\":\"complete\",\"operations\":200,\"createdRefs\":%u,\"releasedRefs\":%u,\"ownedOutputs\":%u,\"releasedOutputs\":%u,\"readOnlyDefaultAndSearchListUnchanged\":true,\"managedRuntimePresent\":false,\"bridgeSha256\":\"%s\",\"elapsedSeconds\":%.6f,\"fullSoakGate\":false}\n",created_refs,released_refs,all_created,all_released,BRIDGE_SHA,elapsed());fflush(stdout);return 0;
}
@@ -0,0 +1,29 @@
{
"allLaunchedChildrenConfirmedReaped": true,
"reapOutcomes": [
{
"processId": 65330,
"confirmedReaped": true,
"exitCode": 0
},
{
"processId": 65334,
"confirmedReaped": true,
"exitCode": 0
},
{
"processId": 65338,
"confirmedReaped": true,
"exitCode": 0
},
{
"processId": 65339,
"confirmedReaped": true,
"exitCode": 1
}
],
"privateMaterialOverwrittenThenUnlinkedAfterChildReaping": true,
"physicalSecureErasureClaimed": false,
"status": "passed",
"ownedFixtureRootRemoved": true
}
@@ -0,0 +1,37 @@
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportControl.c:41:42: warning: 'SecKeychainGetTypeID' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
41 | if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return errSecParam;
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:270:10: note: 'SecKeychainGetTypeID' has been explicitly marked deprecated here
270 | CFTypeID SecKeychainGetTypeID(void)
| ^
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportControl.c:43:21: warning: 'SecKeychainGetPath' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
43 | OSStatus status=SecKeychainGetPath(keychain,&length,path);
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:474:10: note: 'SecKeychainGetPath' has been explicitly marked deprecated here
474 | OSStatus SecKeychainGetPath(SecKeychainRef __nullable keychain, UInt32 *ioPathLength, char *pathName)
| ^
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportControl.c:54:29: warning: 'SecKeychainCopyDefault' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
54 | OSStatus default_status=SecKeychainCopyDefault(&default_keychain);
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:384:10: note: 'SecKeychainCopyDefault' has been explicitly marked deprecated here
384 | OSStatus SecKeychainCopyDefault(SecKeychainRef * __nonnull CF_RETURNS_RETAINED keychain)
| ^
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportControl.c:58:28: warning: 'SecKeychainCopySearchList' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
58 | OSStatus search_status=SecKeychainCopySearchList(&search);
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:404:10: note: 'SecKeychainCopySearchList' has been explicitly marked deprecated here
404 | OSStatus SecKeychainCopySearchList(CFArrayRef * __nonnull CF_RETURNS_RETAINED searchList)
| ^
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportControl.c:145:42: warning: 'SecKeychainGetTypeID' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
145 | if(!keychain||CFGetTypeID(keychain)!=SecKeychainGetTypeID())return 0;
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:270:10: note: 'SecKeychainGetTypeID' has been explicitly marked deprecated here
270 | CFTypeID SecKeychainGetTypeID(void)
| ^
<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportControl.c:166:47: warning: 'SecKeychainGetTypeID' is deprecated: first deprecated in macOS 10.10 - SecKeychain is deprecated [-Wdeprecated-declarations]
166 | int type_ok=!result||CFGetTypeID(result)==SecKeychainGetTypeID();
| ^
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecKeychain.h:270:10: note: 'SecKeychainGetTypeID' has been explicitly marked deprecated here
270 | CFTypeID SecKeychainGetTypeID(void)
| ^
6 warnings generated.
@@ -0,0 +1,59 @@
{
"sourcesSha256": {
"NativeImportControl.c": "d3f8d3ed0a4e25098bc42c3bb8dca8650f2557ee8c180b61877814609ced73df",
"ImportSeed.cs": "feaac1c93bdb0f4e59bd0a9fb213d6de53cb89ece7393356c9d12d318b395c1b",
"native_import.py": "2a2e8b34d351e134e91df16569437893b9bf202966ee98267a81b0030a22111d",
"pure_apple.py": "1b2148f1c20578529355379ab94b439931d7241608a5d5effce8fc86f4af7bc6"
},
"nativeBridgeSha256": "0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c",
"managedCryptographySha256": "f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a",
"sdkSeedSha256": {
"Opc.Ua.Client.dll": "620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd",
"Opc.Ua.Configuration.dll": "6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7",
"Opc.Ua.Core.Schema.dll": "51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e",
"Opc.Ua.Core.Types.dll": "a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96",
"Opc.Ua.Core.dll": "9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a",
"Opc.Ua.Security.Certificates.dll": "7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0",
"Opc.Ua.Server.dll": "930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e",
"Opc.Ua.Types.dll": "c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1"
},
"compileCommand": [
"clang",
"-std=c11",
"-O0",
"-g",
"-Wall",
"-Wextra",
"<user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/scripts/validation/opcua-lifecycle-memory/native-retention/NativeImportControl.c",
"-framework",
"Security",
"-framework",
"CoreFoundation",
"-o",
"/private/tmp/opcua2-native-import-20261110a/NativeImportControl"
],
"compiler": "Apple clang version 21.0.0 (clang-2100.0.123.102)\nTarget: arm64-apple-darwin25.5.0\nThread model: posix\nInstalledDir: /Library/Developer/CommandLineTools/usr/bin\n",
"os": "ProductName:\t\tmacOS\nProductVersion:\t\t26.5.2\nBuildVersion:\t\t25F84\n",
"architecture": "arm64",
"hostDotnet": ".NET SDK:\n Version: 10.0.403\n Commit: 5a2162bea6\n Workload version: 10.0.400-manifests.a00bebb6\n MSBuild version: 18.9.15+5a2162bea\n\nRuntime Environment:\n OS Name: Mac OS X\n OS Version: 26.5\n OS Platform: Darwin\n RID: osx-arm64\n Base Path: /opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk/10.0.403/\n\n.NET workloads installed:\nThere are no installed workloads to display.\nConfigured to use workload sets when installing new manifests.\nNo workload sets are installed. Run \"dotnet workload restore\" to install a workload set.\n\nHost:\n Version: 10.0.14\n Architecture: arm64\n Commit: 0427b9abbe\n\n.NET SDKs installed:\n 10.0.403 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/sdk]\n\n.NET runtimes installed:\n Microsoft.AspNetCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.AspNetCore.App]\n Microsoft.NETCore.App 10.0.14 [/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App]\n\nOther architectures found:\n None\n\nEnvironment variables:\n Not set\n\nglobal.json file:\n <user-home>/.codex/worktrees/opcua2-client-lifecycle/OtOpcUa/global.json\n\nLearn more:\n https://aka.ms/dotnet/info\n\nDownload .NET:\n https://aka.ms/dotnet/download\n",
"repetitions": 200,
"stages": [
"before",
"first",
"repeated",
"idle"
],
"idleSeconds": 5,
"fullSoakGate": false,
"managedCallerInMeasuredChild": false,
"managedPfxParsingEmulated": false,
"legacyKeyLifetime": "Explicit CFRelease after identity disposal; managed local has no explicit using scope.",
"wrapperNativeCopyCallsPerOperation": 9,
"checkpointDeadlineSeconds": 180,
"deadlineScope": "Checkpoint/child deadline; in-progress bounded probes and bounded reap add overhead.",
"defaultOrSearchListSetters": false,
"fixtureCreationUmask": "0077",
"binarySha256": "e5c5caf2a8f882f82b738396156b378fd157ebd1a825d462449cd2a0497830fd",
"seedBinarySha256": "1fa7de542aee11acab18dcc8d4096b21fcbc16e4edc1fca2243159dd1176adc0",
"linkedLibraries": "/private/tmp/opcua2-native-import-20261110a/NativeImportControl:\n\t/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 61901.101.4)\n\t/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 4424.1.255)\n\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)\n"
}
@@ -0,0 +1,4 @@
{"kind":"fingerprint","stage":"before","defaultStatus":0,"defaultPathStatus":0,"searchStatus":0,"searchPathStatus":0,"defaultSha256":"08c2c77dd8eaf8042b4020991ea06c6215cc391532869bbf6ced2cf1126c5e63","searchSha256":"c05fae700986dc7a6a147a938ef2adc4a274173420e137929ed58ad986eb2a3c","searchCount":2,"ownedQueryReferencesReleased":2}
{"kind":"checkpoint","mode":"native-bridge-import","stage":"before","operations":0,"processId":65339,"utc":"2026-11-10T22:25:23.068Z","elapsedSeconds":0.006057,"rssBytes":7815168,"createdRefs":0,"releasedRefs":0,"managedRuntimePresent":false}
{"kind":"operation","iteration":1,"certReturn":1,"certStatus":0,"modernReturn":1,"modernErrorCode":0,"modernErrorPresent":false,"createStatus":0,"pathStatus":0,"neverLockStatus":0,"externalReturn":1,"externalErrorCode":0,"externalErrorPresent":false,"legacyReturn":1,"legacyStatus":0,"moveReturn":1,"moveStatus":0,"copyCertStatus":-2147483648,"deleteStatus":0,"privatePathVerified":true,"deleteDuringFinalIdentityUntrack":false,"fallbackCleanup":false,"logicalOwnersRemaining":0,"ownedOutputs":9,"releasedOutputs":9,"failed":true,"copies":[{"stage":"import-cert-track","status":0,"returnedRef":false,"privateAssociation":false},{"stage":"move-outer-copy","status":0,"returnedRef":false,"privateAssociation":false},{"stage":"move-cert-untrack","status":0,"returnedRef":false,"privateAssociation":false},{"stage":"move-cert-track","status":0,"returnedRef":true,"privateAssociation":true},{"stage":"move-identity-track","status":0,"returnedRef":false,"privateAssociation":false},{"stage":"release-original-cert","status":0,"returnedRef":true,"privateAssociation":true},{"stage":"release-identity","status":0,"returnedRef":false,"privateAssociation":false}]}
{"kind":"failed","message":"Native bridge import operation failed after ownership cleanup","createdRefs":1,"releasedRefs":1,"elapsedSeconds":1.172310}
@@ -0,0 +1,9 @@
Determining projects to restore...
Restored /private/tmp/opcua2-native-import-20261110a/ImportSeed.csproj (in 31 ms).
ImportSeed -> /private/tmp/opcua2-native-import-20261110a/bin/Release/net10.0/ImportSeed.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:01.25
@@ -0,0 +1 @@
{"kind":"seed","leafSha256":"31a4b6181cc10f0bc862a83199688b35d3c5e2590a2c00ab4cae6c3b1dc3d179","runtime":"10.0.14","sdkVersion":"2.0.1.0","managerDisposed":true,"certificateCoresOutstanding":0,"keyFormat":"PKCS1-private-RSA","privateFileMode":"0600","cryptographyRuntime":{"informationalVersion":"10.0.14\u002B0427b9abbead3fa64c6655eb4290c537397131fa","sha256":"f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a"}}
@@ -0,0 +1,45 @@
{
"status": "failed-partial-control-model",
"projection": "Replace local user-home prefix only; raw receipts unchanged.",
"rawOutput": "/tmp/opcua2-native-import-20261110a",
"rawSha256": {
"samples.jsonl": "06ca824cf08e2c26e595a4e70091f2f5a953ac3022908966ae9ee99987c1043d",
"provenance.json": "ad87286e153b8235aa13f3a60e9bc973f9c8886a038e947ed11304361e236590",
"native-build.log": "2fd25e9c530541c224872a28cf03b7d720be28fdd0216946b533d6292aad1a75",
"seed-build.log": "bd5eb3fbfeb649aa12254fa52689a778c97364c832bee0b6968f0432da129022",
"seed.jsonl": "8b4d9cb09fc134ffed468d4a0acc12d317cb9a77c0f90569ef52e83577baf333",
"fixture-cleanup.json": "0d5c8fe16558d2a21406c88d62ed87690649d3deac7bd333d8741672a23f86c7",
"fixture-before-harness-cleanup.json": "eaaf36a5e7f7d1d3635e987d8f9a2600ce55f6482011ec9a7abf3e4dd759d9c4"
},
"selectedProjectionSha256": {
"samples.jsonl": "06ca824cf08e2c26e595a4e70091f2f5a953ac3022908966ae9ee99987c1043d",
"provenance.json": "c6f6453edfb0c0601099e6275303359abec52f7566a47961a640bd97d554cbe9",
"native-build.log": "05fac84aff441e7a6b5ce2c3ac1d68cc3705b85495c39dd339964168335c5316",
"seed-build.log": "bd5eb3fbfeb649aa12254fa52689a778c97364c832bee0b6968f0432da129022",
"seed.jsonl": "8b4d9cb09fc134ffed468d4a0acc12d317cb9a77c0f90569ef52e83577baf333",
"fixture-cleanup.json": "0d5c8fe16558d2a21406c88d62ed87690649d3deac7bd333d8741672a23f86c7",
"fixture-before-harness-cleanup.json": "eaaf36a5e7f7d1d3635e987d8f9a2600ce55f6482011ec9a7abf3e4dd759d9c4"
},
"rawLocalOnlySnapshotsSha256": {
"heap-before.txt": "de4896c196fcf96e58933716049772e523e268be932cef7f9a76cf270f71cd79",
"lsof-before.txt": "f12e1115e87c4110c2961cb61c0b28ea3395c2ae780a72b431d71d4bfdd2a965"
},
"retainedInstalledBinaryInspectionSha256": {
"opcua2-native-runtime-loader-decompiled.cs": "89434500d1723f419e83ff45fcb6e44e73b58847f4201265613587ad82a79424",
"opcua2-native-runtime-pkcs12key-decompiled.cs": "60af9993d62032adf6be178efd93eef0f9c1a7a1bb2e091938d7171ab12875e2",
"opcua2-native-runtime-pkcs12key-base-decompiled.cs": "cf1e412c9752822610e6e9c96341d2b83052e2e284edc3d1c0086e95893a39b0",
"opcua2-native-runtime-rsa-decompiled.cs": "a4d9bdc8c0d78dac63f19639fb1da319e209a0f4af324edefd0fac8bc4b3becc",
"opcua2-native-runtime-applepal-decompiled.cs": "94e7ead2224719e0102983e0a13c97a9c713018640d73a7153ffea6f732312bf",
"opcua2-native-runtime-appleinterop-decompiled.cs": "da9b4ad8bd114e5b58a82607b37fbc8bc71f79a3a56dead60c88e718d8758c92",
"opcua2-native-runtime-keychainitem-decompiled.cs": "5fb58039e59d4839198fd74ed98978eed0d57008322489fd08d70d6481dd4fd4",
"opcua2-native-runtime-keyhandle-decompiled.cs": "595f2cf7d1c71a13dbdd21379a0ab072ce7d629753ca01f59e57bf789bec223e",
"opcua2-native-runtime-keypair-decompiled.cs": "623cf239bd9f755c701ba50ceffd4043591538111ca72261af4cd59c97db61c9",
"opcua2-native-runtime-certhandle-decompiled.cs": "d53053dfee192fe336e6fae055199ddcf4340da805ed8744e5e52c66f685e453",
"opcua2-native-runtime-identityhandle-decompiled.cs": "80b94acf27a0fbe38efcdc30c86e003ee2abfea30a3da5e9d535e1373ce97069",
"opcua2-native-runtime-keychain-decompiled.cs": "96c2e996638ffdd17c35483aaece7fdbc499fd004ab549e2e128bf82a64b5dc2",
"opcua2-native-runtime-keychain-base-decompiled.cs": "6f2abdb5779646a9bfd589d8fc765cab2e0290edee8396b2c7b2e6a4470e2bb4",
"opcua2-native-runtime-x509contenttype-decompiled.cs": "bed5256e025ed5b85f04b4cce132fcd7279fb06b5dd377d25f4caf39a8682b60",
"opcua2-native-runtime-apple-disassembly.txt": "75af92a3b05e4076025d43389cb3e51d8f6c8547aa485a4b5984469af7f02935"
},
"noRepeatCompletionOrRetentionInference": true
}
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
"""One fixed-200 native bridge import diagnostic; build/run only after review."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import shutil
import signal
import subprocess
import sys
import time
import xml.etree.ElementTree as ET
from pure_apple import complete_rows, save, snapshot, STAGES
HERE = Path(__file__).resolve().parent
RUNTIME = Path('/opt/homebrew/Cellar/dotnet/10.0.403/libexec/shared/Microsoft.NETCore.App/10.0.14')
BRIDGE = RUNTIME / 'libSystem.Security.Cryptography.Native.Apple.dylib'
BRIDGE_SHA = '0333994e217a3d111d36d7f31460eea69a286f9fc819e2d7c746df13d2aec46c'
CRYPTO_SHA = 'f4957d7218a004a27bf8964e45147df6a88092c6e7344582082840d62c6c579a'
DOTNET = '/opt/homebrew/Cellar/dotnet/10.0.403/libexec/dotnet'
EXPECTED_SDK = {
'Opc.Ua.Client.dll': '620da406fabdbc75d11f9827ea7e39d2d3c762471bd2264b0d11f1d6cd9247cd',
'Opc.Ua.Configuration.dll': '6ed6a594c93c3ff35d1a16539a9fe87c1e2640549cc2f48ed6bae333dc8bf0e7',
'Opc.Ua.Core.Schema.dll': '51cfad48657d95d38074bb88be29aea6f5b361a07397247d58838d066b99772e',
'Opc.Ua.Core.Types.dll': 'a7dd141b8dabe8848ff28c2fb8e16a85a0508e6a49e6b1a1c1e267175b99af96',
'Opc.Ua.Core.dll': '9d0857aa6b7f3c49037d4ea557b4da8edf07e146069464481783e66c5c9cd35a',
'Opc.Ua.Security.Certificates.dll': '7f4969aceb2f0db5328c6882cb4b117342f3b18905c38acdba600683860c4fd0',
'Opc.Ua.Server.dll': '930e714a2f6e15d393840bcdc95e1afa8012bc740c3738a655f958142721864e',
'Opc.Ua.Types.dll': 'c542f69882f4e8a4ebd5ccc28a3881d6967c7ffb6dcc3c56dc6c776f39c2ede1',
}
CHILDREN = []
REAP_OUTCOMES = []
COPY_STAGES = ['import-cert-track', 'move-outer-copy', 'move-cert-untrack', 'move-cert-track',
'move-identity-track', 'identity-cert-track', 'release-original-cert',
'release-copied-cert', 'release-identity']
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def reap(process):
outcome = {'processId': process.pid, 'confirmedReaped': False}
try:
if process.poll() is None:
try:
os.killpg(process.pid, signal.SIGTERM)
except ProcessLookupError:
pass
try:
process.wait(timeout=10)
except subprocess.TimeoutExpired:
try:
os.killpg(process.pid, signal.SIGKILL)
except ProcessLookupError:
pass
process.wait(timeout=5)
outcome['exitCode'] = process.wait(timeout=0)
outcome['confirmedReaped'] = True
except Exception as error:
outcome['errorType'] = type(error).__name__
raise
finally:
REAP_OUTCOMES.append(outcome)
def bounded(command, log, timeout, env=None):
with log.open('w') as stream:
process = subprocess.Popen(command, stdout=stream, stderr=subprocess.STDOUT,
env=env, start_new_session=True)
CHILDREN.append(process)
try:
status = process.wait(timeout=timeout)
if status:
raise RuntimeError('Bounded subprocess failed: ' + log.name)
finally:
reap(process)
def wipe_owned(root):
"""Best-effort caller-owned file overwrite/unlink; no physical secure-erasure claim."""
if not root.exists():
return
files = [p for p in root.rglob('*') if p.is_file() and not p.is_symlink()]
if len(files) > 100 or any(p.stat().st_size > 1024 * 1024 or p.stat().st_nlink != 1 for p in files):
raise RuntimeError('Unexpected owned fixture cleanup size/link layout; preserve private root')
for path in files:
size = path.stat().st_size
with path.open('r+b') as stream:
stream.write(bytes(size)); stream.flush(); os.fsync(stream.fileno())
shutil.rmtree(root)
def main():
os.umask(0o077) # This collector and its children only; includes normal SDK seed PFX files.
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--references', required=True, type=Path)
parser.add_argument('--output', required=True, type=Path, help='Fresh private output directory')
args = parser.parse_args()
if sys.platform != 'darwin' or os.uname().machine != 'arm64':
parser.error('Pinned diagnostic requires macOS arm64')
if sha(BRIDGE) != BRIDGE_SHA or sha(RUNTIME / 'System.Security.Cryptography.dll') != CRYPTO_SHA:
raise RuntimeError('Installed native/managed cryptography identity changed')
references = args.references.resolve()
if {p.name: sha(p) for p in references.glob('Opc.Ua*.dll')} != EXPECTED_SDK:
raise RuntimeError('Pinned SDK2 seed binary set changed')
output = args.output.resolve(); output.mkdir(parents=True, exist_ok=False); os.chmod(output, 0o700)
binary = output / 'NativeImportControl'
command = ['clang', '-std=c11', '-O0', '-g', '-Wall', '-Wextra', str(HERE / 'NativeImportControl.c'),
'-framework', 'Security', '-framework', 'CoreFoundation', '-o', str(binary)]
provenance = {'sourcesSha256': {name: sha(HERE / name) for name in ['NativeImportControl.c', 'ImportSeed.cs', 'native_import.py', 'pure_apple.py']},
'nativeBridgeSha256': BRIDGE_SHA, 'managedCryptographySha256': CRYPTO_SHA, 'sdkSeedSha256': EXPECTED_SDK,
'compileCommand': command, 'compiler': subprocess.check_output(['clang', '--version'], text=True, timeout=10),
'os': subprocess.check_output(['sw_vers'], text=True, timeout=10), 'architecture': os.uname().machine,
'hostDotnet': subprocess.check_output([DOTNET, '--info'], text=True, timeout=10),
'repetitions': 200, 'stages': STAGES, 'idleSeconds': 5, 'fullSoakGate': False,
'managedCallerInMeasuredChild': False, 'managedPfxParsingEmulated': False,
'legacyKeyLifetime': 'Explicit CFRelease after identity disposal; managed local has no explicit using scope.',
'wrapperNativeCopyCallsPerOperation': 9, 'checkpointDeadlineSeconds': 180,
'deadlineScope': 'Checkpoint/child deadline; in-progress bounded probes and bounded reap add overhead.',
'defaultOrSearchListSetters': False, 'fixtureCreationUmask': '0077'}
save(output / 'provenance.json', provenance)
bounded(command, output / 'native-build.log', 60)
project = ET.Element('Project', Sdk='Microsoft.NET.Sdk'); properties = ET.SubElement(project, 'PropertyGroup')
for name, value in {'OutputType': 'Exe', 'TargetFramework': 'net10.0', 'ImplicitUsings': 'enable', 'Nullable': 'enable',
'EnableDefaultCompileItems': 'false', 'TreatWarningsAsErrors': 'true', 'UseSharedCompilation': 'false'}.items():
ET.SubElement(properties, name).text = value
items = ET.SubElement(project, 'ItemGroup'); ET.SubElement(items, 'FrameworkReference', Include='Microsoft.AspNetCore.App')
ET.SubElement(items, 'Compile', Include=str(HERE / 'ImportSeed.cs'))
for name in EXPECTED_SDK:
ref = ET.SubElement(items, 'Reference', Include=Path(name).stem); ET.SubElement(ref, 'HintPath').text = str(references / name)
ET.indent(project); ET.ElementTree(project).write(output / 'ImportSeed.csproj', encoding='unicode')
bounded([DOTNET, 'build', str(output / 'ImportSeed.csproj'), '-c', 'Release', '-m:1', '--disable-build-servers', '--nologo'], output / 'seed-build.log', 120)
provenance['binarySha256'] = sha(binary)
provenance['seedBinarySha256'] = sha(output / 'bin/Release/net10.0/ImportSeed.dll')
provenance['linkedLibraries'] = subprocess.check_output(['otool', '-L', str(binary)], text=True, timeout=10)
save(output / 'provenance.json', provenance)
task = output / 'task'; task.mkdir(mode=0o700)
for name in ['seed', 'seed-tmp', 'seed-data', 'tmp']:
(task / name).mkdir(mode=0o700)
try:
bounded([DOTNET, str(output / 'bin/Release/net10.0/ImportSeed.dll'), str(task / 'seed')], output / 'seed.jsonl', 75,
env=dict(os.environ, TMPDIR=str(task / 'seed-tmp'), XDG_DATA_HOME=str(task / 'seed-data')))
seeds = complete_rows(output / 'seed.jsonl')
if len(seeds) != 1 or seeds[0]['kind'] != 'seed' or seeds[0]['sdkVersion'] != '2.0.1.0' or seeds[0]['cryptographyRuntime']['sha256'] != CRYPTO_SHA:
raise RuntimeError('Seed/runtime count or identity mismatch')
if sha(task / 'seed/leaf.der') != seeds[0]['leafSha256']:
raise RuntimeError('Seed leaf file identity mismatch')
if any(p.stat().st_mode & 0o077 for p in (task / 'seed').rglob('*') if p.is_file()):
raise RuntimeError('Seed fixture file permissions are not private0600')
for name in ['pki']:
wipe_owned(task / 'seed' / name)
wipe_owned(task / 'seed-tmp'); wipe_owned(task / 'seed-data')
if sha(BRIDGE) != BRIDGE_SHA:
raise RuntimeError('Native bridge changed before measured execution')
started = time.monotonic()
with (output / 'samples.jsonl').open('w') as log:
process = subprocess.Popen([str(binary), str(task), str(BRIDGE)], stdout=log, stderr=subprocess.STDOUT,
env=dict(os.environ, TMPDIR=str(task / 'tmp')), start_new_session=True)
CHILDREN.append(process)
try:
deadline = time.monotonic() + 180; samples = []
for stage in STAGES:
while True:
rows = complete_rows(output / 'samples.jsonl')
if any(r['kind'] == 'failed' for r in rows):
raise RuntimeError('Native import failed; preserve all status/ownership rows')
checkpoints = [r for r in rows if r['kind'] == 'checkpoint' and r.get('stage') == stage]
if checkpoints:
samples.append({**checkpoints[0], **snapshot(process.pid, task, output, stage)})
(task / ('continue-' + stage)).touch(); break
if process.poll() is not None or time.monotonic() >= deadline:
raise RuntimeError('Native checkpoint exceeded bound: ' + stage)
time.sleep(.05)
status = process.wait(timeout=15)
rows = complete_rows(output / 'samples.jsonl')
operations = [r for r in rows if r['kind'] == 'operation']; fingerprints = [r for r in rows if r['kind'] == 'fingerprint']
complete = [r for r in rows if r['kind'] == 'complete']
if status or len(rows) != 207 or len(operations) != 200 or len(fingerprints) != 2 or len(complete) != 1 or [r['iteration'] for r in operations] != list(range(1, 201)):
raise RuntimeError('Native fixed operation/completion counts failed')
for row in operations:
if row['failed'] or row['fallbackCleanup'] or not row['privatePathVerified'] or not row['deleteDuringFinalIdentityUntrack'] or row['logicalOwnersRemaining'] or row['modernErrorPresent'] or row['externalErrorPresent'] or row['ownedOutputs'] != row['releasedOutputs']:
raise RuntimeError('Native ownership order/cleanup failed')
if [c['stage'] for c in row['copies']] != COPY_STAGES or any(c['status'] for c in row['copies']) or [c['returnedRef'] for c in row['copies']] != [False] * 3 + [True] * 6:
raise RuntimeError('Native wrapper-copy sequence failed')
if any(row[k] for k in ['certStatus', 'createStatus', 'pathStatus', 'neverLockStatus', 'legacyStatus', 'moveStatus', 'copyCertStatus', 'deleteStatus', 'modernErrorCode', 'externalErrorCode']) or any(row[k] != 1 for k in ['certReturn', 'modernReturn', 'externalReturn', 'legacyReturn', 'moveReturn']):
raise RuntimeError('Native bridge statuses failed')
if sha(task / 'seed/leaf.der') != seeds[0]['leafSha256'] or complete[0]['operations'] != 200 or complete[0]['bridgeSha256'] != BRIDGE_SHA or complete[0]['ownedOutputs'] != complete[0]['releasedOutputs']:
raise RuntimeError('Native identity/completion ownership guard failed')
if any(samples[-1][k] for k in ['ownedKeychainFdCount', 'ownedKeychainFileCount']):
raise RuntimeError('Private keychain files/descriptors retained')
save(output / 'result.json', {'status': 'passed', 'seed': seeds[0], 'samples': samples, 'fingerprints': fingerprints,
'complete': complete[0], 'wallSecondsIncludingProbes': time.monotonic() - started, 'fullSoakGate': False,
'nativeInventoryIsNotOpenHandlesOrExclusiveRss': True, 'measuredChildManagedRuntimePresent': False})
finally:
reap(process)
finally:
save(output / 'fixture-before-harness-cleanup.json', {'remainingOwnedKeychainFiles': sum(p.is_file() and '.keychain' in p.name for p in (task / 'tmp').rglob('*'))})
all_reaped = all(child.returncode is not None for child in CHILDREN)
cleanup = {'allLaunchedChildrenConfirmedReaped': all_reaped, 'reapOutcomes': REAP_OUTCOMES,
'privateMaterialOverwrittenThenUnlinkedAfterChildReaping': False, 'physicalSecureErasureClaimed': False}
if not all_reaped:
cleanup.update(status='failed-preserved-private-fixture', ownedFixtureRootRemoved=False)
save(output / 'fixture-cleanup.json', cleanup)
raise RuntimeError('A launched child was not reaped; private fixture preserved without overwrite/unlink')
try:
wipe_owned(task)
cleanup.update(status='passed', ownedFixtureRootRemoved=not task.exists(),
privateMaterialOverwrittenThenUnlinkedAfterChildReaping=True)
except Exception as error:
cleanup.update(status='failed', errorType=type(error).__name__, ownedFixtureRootRemoved=not task.exists())
raise
finally:
save(output / 'fixture-cleanup.json', cleanup)
print(output)
if __name__ == '__main__':
main()