#!/usr/bin/env python3 """One fixed-200 native bridge import diagnostic; build/run only after review.""" import argparse from collections import Counter 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 validate_operation(row): if row['failed'] or row['fallbackCleanup'] or not row['privatePathVerified'] or not row['importStateReleased'] or row['logicalOwnersRemaining'] or row['modernErrorPresent'] or row['externalErrorPresent']: raise RuntimeError('Native ownership/cleanup guard 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') leaf_check = row['iteration'] in [1, 200] if row['leafIdentityChecked'] != leaf_check or row['leafIdentityMatches'] != leaf_check: raise RuntimeError('First/last native identity checks failed') copies = row['copies'] if [c['stage'] for c in copies] != COPY_STAGES or any(c['status'] or c['returnedRef'] != c['privateAssociation'] for c in copies): raise RuntimeError('Native wrapper-copy sequence/status/path failed') # Independently reconstruct ownership from observed associations, with no fixed non-null pattern. actions = ['track', 'lookup', 'untrack', 'track', 'track', 'track', 'untrack', 'untrack', 'untrack'] items = ['original', None, 'original', 'original', 'identity', 'copied', 'original', 'copied', 'identity'] tracked = {'original': False, 'copied': False, 'identity': False} logical = 0 native_owner = False deletion = None live_at_delete = None for index, (copy, action, item) in enumerate(zip(copies, actions, items)): if index == 1: # Fresh native keychain created between DER TrackItem and Move wrapper. logical = 1 native_owner = True if index == 7: # Initial import state releases after disposing the original certificate. if row['importOwnersBefore'] != logical or not native_owner: raise RuntimeError('Initial import owner release was misplaced') logical -= 1 if row['importOwnersAfter'] != logical or logical < 0: raise RuntimeError('Initial import owner accounting failed') if logical == 0: deletion = 'release-import-state' native_owner = False live_at_delete = {'copy': False, 'originalCertificate': False, 'copiedCertificate': True, 'identity': True, 'legacyKey': True} if copy['action'] != action or copy['logicalOwnersBefore'] != logical or copy['nativeOwnerBefore'] != native_owner: raise RuntimeError('Native wrapper ownership before copy does not reconcile') associated = copy['privateAssociation'] if action == 'track' and associated: if tracked[item] or not native_owner: raise RuntimeError('Track tried to duplicate or use a retired native owner') tracked[item] = True logical += 1 elif action == 'untrack': if tracked[item]: if not associated or not native_owner: raise RuntimeError('Previously tracked item lost its association before Untrack') tracked[item] = False logical -= 1 if logical == 0: if deletion is not None: raise RuntimeError('Duplicate native deletion') deletion = copy['stage'] native_owner = False live_at_delete = {'copy': True, 'originalCertificate': index == 6, 'copiedCertificate': index in [6, 7], 'identity': True, 'legacyKey': True} elif associated and native_owner: raise RuntimeError('Untracked item would consume a still-registered owner') if logical < 0 or copy['logicalOwnersAfter'] != logical or copy['nativeOwnerAfter'] != native_owner: raise RuntimeError('Native wrapper ownership after copy does not reconcile') if logical or native_owner or any(tracked.values()) or deletion is None or row['deleteStage'] != deletion or row['deleteLiveReferences'] != live_at_delete: raise RuntimeError('Native logical owners or actual zero-owner delete stage did not reconcile') expected_outputs = 8 + sum(c['returnedRef'] for c in copies) + int(leaf_check) if row['ownedOutputs'] != expected_outputs or row['releasedOutputs'] != expected_outputs: raise RuntimeError('Native returned CF ownership did not reconcile') 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, 'logicalOwnersFromObservedPrivateAssociationsOnly': True, '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: validate_operation(row) if sha(task / 'seed/leaf.der') != seeds[0]['leafSha256'] or complete[0]['operations'] != 200 or complete[0]['bridgeSha256'] != BRIDGE_SHA or complete[0]['ownedOutputs'] != sum(r['ownedOutputs'] for r in operations) or complete[0]['releasedOutputs'] != sum(r['releasedOutputs'] for r in operations) or complete[0]['createdRefs'] != 200 or complete[0]['releasedRefs'] != 200: 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, 'actualDeletionStages': dict(Counter(r['deleteStage'] for r in operations)), 'returnedCopyReferencesByStage': {stage: sum(c['returnedRef'] for r in operations for c in r['copies'] if c['stage'] == stage) for stage in COPY_STAGES}}) 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()