109 lines
8.1 KiB
Python
109 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Qualify actual owner root enforcement and independent identity refusal."""
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import struct
|
|
import sys
|
|
import uuid
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / 'kits/XnuPrelink'))
|
|
import native_prelink as np
|
|
|
|
|
|
def require(value, message):
|
|
if not value:
|
|
raise SystemExit(message)
|
|
|
|
|
|
original = (ROOT / '.cache/firmware/kernelcache.macho/kernelcache.release.ipad7.decompressed').read_bytes()
|
|
require(hashlib.sha256(original).hexdigest() == 'f18b4df865405f2a287b84977d7aca745e056d656d9fc9df1662970ce2969de7',
|
|
'Require the supplied ordinary-ARM64 target')
|
|
old = np.macho(original)
|
|
policy = (ROOT / 'build/verified-root-os/manifest.bin').read_bytes()
|
|
wrong_policy = (ROOT / 'build/verified-root-os-xnu-wrong-uuid/wrong-uuid-manifest.bin').read_bytes()
|
|
anchor = (ROOT / 'build/verified-root-os/trusted-key.bin').read_bytes()
|
|
require(policy[544:] == wrong_policy[544:] and policy[:40] == wrong_policy[:40] and policy[56:160] == wrong_policy[56:160] and
|
|
policy[40:56] != wrong_policy[40:56], 'Negative policy changes only signed UUID/signature, preserving every original hash')
|
|
require(str(uuid.UUID(bytes=policy[40:56])).upper() == '04769D26-2CA0-4825-B443-E2E5F88300F2' and
|
|
struct.unpack_from('<QQ', policy, 24) == (6077580800, 1483785), 'Require complete exact original OS media policy')
|
|
host = json.loads((ROOT / 'build/verified-root-os/verification.json').read_text())
|
|
require(host['real_c_signature_and_block_verification'] and host['blocks_verified'] == 1483785 and
|
|
host['image_sha256'] == 'e5634d04a8ac35f024845f583394e0a35deb26a04cb0c00286a8674e25a9a4f2',
|
|
'Require separate complete original host data verification')
|
|
|
|
cases = (
|
|
('default-refusal', 'native-owner-root-trust-default-candidate', 'aca84655e63702d77ea0be2004c8ccc74bb3f334597906aed08eb9621a50cfaa', 0, 1, 0),
|
|
('accepted', 'native-owner-root-trust-candidate', 'bd231941e1ff79c2f1917f99c2e5f05720d5f0a1fdc3d325f732609cb9ecfd63', 1, 1, 1),
|
|
('wrong-uuid-refusal', 'native-owner-root-wrong-uuid-candidate', '4e719827eae5a48f73158991ff6d142f9baa4a7bd8994bdaea43d04bc416ff4e', 1, 0, 0),
|
|
('original-continuation', 'native-owner-root-trust-candidate', 'bd231941e1ff79c2f1917f99c2e5f05720d5f0a1fdc3d325f732609cb9ecfd63', 1, 1, 1),
|
|
)
|
|
reports = {}
|
|
for name, candidate, identity, enabled, decision, accepted in cases:
|
|
d = json.loads((ROOT / ('build/darwinboot-owner-root-' + name) / 'gdb-stop.json').read_text())
|
|
reports[name] = d
|
|
plan = json.loads((ROOT / 'build/xnu-prelink' / (candidate + '.json')).read_text())
|
|
kernel = (ROOT / 'build/xnu-prelink' / (candidate + '.macho')).read_bytes()
|
|
require(d['kernel_sha256'] == plan['candidate_sha256'] == hashlib.sha256(kernel).hexdigest() == identity and
|
|
not d['bounded_timeout_interrupted'] and len(plan['components']) == 14, 'Require exact bounded native candidates')
|
|
meta = np.macho(kernel)
|
|
exports = np.exports(kernel, meta)
|
|
for symbol, wanted in (('_verified_root_manifest', wrong_policy if name == 'wrong-uuid-refusal' else policy),
|
|
('_verified_root_key', anchor)):
|
|
address = next(a for a, names in exports.items() if symbol in names)
|
|
at = np.backed_offset(meta['segments'], address, len(wanted), len(kernel))
|
|
require(kernel[at:at + len(wanted)] == wanted, 'Require the actual identical anchor and identified signed policy')
|
|
for extent in plan['extents']:
|
|
if extent['segment'] != '__TEXT_EXEC':
|
|
continue
|
|
at = np.backed_offset(meta['segments'], extent['va'], extent['size'], len(kernel))
|
|
prev = np.backed_offset(old['segments'], extent['va'] - plan['slide'], extent['size'], len(original))
|
|
require(kernel[at:at + extent['size']] == original[prev:prev + extent['size']], 'Original executable code must remain unchanged')
|
|
regs = {k: int(v, 16) for k, v in d['system_registers'].items()}
|
|
require(regs['TCR_EL1'] >> 14 & 3 == 0 and regs['TCR_EL1'] >> 30 & 3 == 2 and regs['SCTLR_EL1'] & 1 and
|
|
regs['TTBR0_EL1'] and regs['TTBR1_EL1'], 'Require actual native hardware 4K, not source constants')
|
|
require(d['original_crypto_initialized_observed'] and d['original_crypto_table_owner_observed'] and
|
|
d['original_amfi_table_owner_observed'] and d['virtio_block_input']['read_only'], 'Original AMFI/crypto must remain active')
|
|
state, io, integrity = d['owner_root_binding_audit'], d['native_virtio_globals'], d['native_virtio_integrity']
|
|
require(state['_native_owner_root_apple_autherr'] == 80 and state['_native_owner_root_readonly'] == 1 and
|
|
state['_native_owner_root_trust_enabled'] == enabled and state['_native_owner_root_decision'] == decision and
|
|
state['_native_owner_root_accepted'] == accepted and state['boot_root_accepted'] == bool(accepted),
|
|
'Actual enforcement must retain Apple EAUTH and independently require real owner proof')
|
|
require(state['_native_owner_root_mount_device'] == state['_native_owner_root_selected_device'] ==
|
|
state['_native_verified_block_root_device'] != 0 and state['_native_verified_block_root_media_matches'] == 1 and
|
|
state['_native_verified_block_root_provider_depth'] == 9 and state['_native_verified_block_root_uuid_match'] == decision and
|
|
state['_native_verified_block_wrong_uuid_refused'] == 1 and
|
|
state['actual_uuid'] == '04769D26-2CA0-4825-B443-E2E5F88300F2', 'Require actual mounted dev_t, unique IOMedia and typed verified provider')
|
|
require(io['_native_virtio_ready'] == 1 and io['_native_virtio_sectors'] == 11870275 and
|
|
io['_native_virtio_read_requests'] >= 167 and io['_native_virtio_read_failures'] == 0 and
|
|
integrity['_native_virtio_manifest_ready'] == 1 and integrity['_native_virtio_verified_blocks'] >= 329 and
|
|
integrity['_native_virtio_integrity_failures'] == 0, 'Real signed policy and every consumed DMA snapshot must qualify')
|
|
if name in ('accepted', 'wrong-uuid-refusal'):
|
|
copies = list(d['indirect_memory'].values())
|
|
require(len(copies) == 2, 'Require actual private policy/key copies')
|
|
for copy in copies:
|
|
t = copy['translation']
|
|
require(t['leaf_level'] == 3 and t['leaf_bytes'] == 4096 and t['ap'] == 2 and t['pxn'] and t['uxn'],
|
|
'Actual policy/key copies must retain read-only NX native-4K PTEs')
|
|
if not accepted:
|
|
require(d['stop_symbol'] == '_panic' and d['argument_memory']['x0'].startswith('rootvp not authenticated after mounting'),
|
|
'Default-off and genuine signed wrong-identity policies must reach actual enforcement refusal')
|
|
|
|
d = reports['accepted']
|
|
require(d['stop_symbol'] == '_native_owner_root_accepted_checkpoint' and d['function_return_stack_matched'] and
|
|
[int(x, 16) for x in d['function_entry_arguments'][:2]] == [80, 1],
|
|
'Observe actual same-stack control flow after enforcement, never a pre-enforcement intent flag')
|
|
d = reports['original-continuation']
|
|
require(d['stop_symbol'] == '_panic' and d['argument_memory']['x0'].startswith('Process 1 exec of %s failed, errno %d') and
|
|
struct.unpack_from('<Q', bytes.fromhex(d['stack_bytes']), 8)[0] == 45 and
|
|
bytes.fromhex(d['stack_pointer_memory']['0']['bytes']).split(b'\0')[0] == b'/sbin/launchd' and
|
|
any(e['symbol'] == '_native_owner_root_accepted_checkpoint' for e in d['trace_events']),
|
|
'Record genuine later original launchd exec ENOTSUP; do not report userspace success')
|
|
binding = json.loads((ROOT / 'build/virtio-mmio-block-verified/bindings.json').read_text())
|
|
require(binding['virtual_methods_checked'] == 190 and binding['concrete_overrides'] == 25 and
|
|
binding['unresolved_original_imports'] == 0, 'Require real IOStorage dispatch ABI')
|
|
print('PASS: native-4K original OS root accepted only through real owner signature, verified I/O and mounted-media identity')
|
|
print('PASS: default-off policy and genuinely signed wrong-volume UUID both refuse at actual root enforcement')
|
|
print('Original AMFI/crypto/code remain active; subsequent original launchd exec returns ENOTSUP=45; hardware/UI remain open')
|