Verify private relative mouse input against native player camera

This commit is contained in:
2026-09-19 18:42:55 +02:00
parent 67de3752fb
commit e10b8799e9
60 changed files with 14049 additions and 21 deletions
+40 -8
View File
@@ -631,6 +631,8 @@ def validate_input_observer(a):
def validate_input_exercise(a):
if getattr(a, 'exercise_relative_look', False) and not getattr(a, 'exercise_player_input', False):
refuse('Relative look requires the existing explicit keyboard exercise')
if not getattr(a, 'exercise_player_input', False):
return
if (a.project not in {'core', 'chat'} or getattr(a, 'packed_core', None)
@@ -642,14 +644,19 @@ def validate_input_exercise(a):
refuse('Input exercise requires --settle >=50')
def input_observer_command(command, project, packed, nonce):
def input_observer_command(command, project, packed, nonce, look=False):
if project not in {'core', 'chat'} or packed is not None:
refuse('Input observation requires loose Core or Chat')
if not re.fullmatch(r'[A-Za-z0-9]{1,32}', nonce):
refuse('Input observer nonce must contain 1..32 ASCII letters/digits')
if any(value.startswith('-errOrdinaryInput') for value in command):
if type(look) is not bool:
refuse('Relative look selection must be boolean')
if any(value.startswith(('-errOrdinaryInput', '-errOrdinaryLook')) for value in command):
refuse('Input observer command already has observer options')
return command + ['-errOrdinaryInputObserve', nonce, '-errOrdinaryInputScreenshots', '1']
options = ['-errOrdinaryInputObserve', nonce, '-errOrdinaryInputScreenshots', '1']
if look:
options += ['-errOrdinaryLookObserve', nonce]
return command + options
def input_observer_evidence(content, nonce):
@@ -819,7 +826,7 @@ def input_observer_result(result, content, nonce):
return result, evidence
def input_exercise_command(command, root, compat, profile, executable, run_id, host_display, display_context):
def input_exercise_command(command, root, compat, profile, executable, run_id, host_display, display_context, look=False):
"""Put the input supervisor around the display inspector, inside Gamescope."""
separator = command.index('--')
if command[:separator].count('--backend') != 1 or command[command.index('--backend')+1] != 'headless':
@@ -830,6 +837,14 @@ def input_exercise_command(command, root, compat, profile, executable, run_id, h
if (not display_context or len(child) < 3 or child[:2] != [sys.executable, str(ownership)]
or not re.fullmatch(r'[A-Za-z0-9]{1,32}', run_id)):
refuse('Input exercise requires the existing scoped display wrapper and observer nonce')
if type(look) is not bool:
refuse('Relative look selection must be boolean')
look_options = [i for i,value in enumerate(child) if value.startswith('-errOrdinaryLook')]
if look:
if len(look_options) != 1 or child[look_options[0]] != '-errOrdinaryLookObserve' or child[look_options[0]+1:look_options[0]+2] != [run_id]:
refuse('Relative look requires its exact native observer nonce')
elif look_options:
refuse('Unexpected native relative-look observer')
report = profile/('input-proof-'+run_id+'.json')
if helper.is_symlink() or not helper.is_file() or report.exists() or report.is_symlink():
refuse('Input exercise requires a regular helper and a new report')
@@ -840,6 +855,9 @@ def input_exercise_command(command, root, compat, profile, executable, run_id, h
wrapper = [sys.executable, str(helper), '--enable-private-keyboard-proof', '--host-display', host_display,
'--prefix', context['prefix'], '--profile', str(profile), '--game-executable', str(executable),
'--report', str(report), '--nonce', run_id, '--']
if look:
context['look_requested'] = True
wrapper.insert(-1, '--enable-private-look-proof')
return command[:separator+1] + wrapper + child, context
@@ -861,6 +879,8 @@ def input_exercise_poll_failure(context):
for key in ('helper_sha256', 'ownership_helper_sha256', 'prefix', 'profile', 'game_executable', 'report', 'command', 'nonce'):
if report.get(key) != context[key]:
return False
if report.get('look_requested', False) != context.get('look_requested', False):
return False
return bool(report.get('error') or report.get('supervision_failed') is True or (
report.get('sequence_completed') is False
and report.get('whole_process_group_cleanup_verified') is False
@@ -902,6 +922,11 @@ def input_exercise_result(result, context, session, observation, display_result)
for key in ('helper_sha256', 'ownership_helper_sha256', 'prefix', 'profile', 'game_executable', 'report', 'command', 'nonce'):
if report.get(key) != context[key]:
refuse('Input report binding mismatch: '+key)
look_requested = context.get('look_requested', False)
if type(report.get('look_requested', False)) is not bool or report.get('look_requested', False) != look_requested:
refuse('Relative look request does not match launch context')
if not look_requested and report.get('look_proof') is not None:
refuse('Unexpected relative mouse evidence in keyboard-only run')
if type(report.get('input_sent')) is bool:
proof['input_sent'] = report['input_sent']
if (report.get('schema') != 1 or report.get('kind') != 'headless-private-keyboard-experiment'
@@ -909,7 +934,7 @@ def input_exercise_result(result, context, session, observation, display_result)
or report.get('hard_supervision') is not True or report.get('supervision_failed')
or report.get('all_attempted_presses_released') is not True
or report.get('error') or report.get('paired_release_errors') != [] or report.get('unconfirmed_held_key') is not None):
refuse('Private keyboard sequence did not complete with confirmed paired release')
refuse('Private input sequence did not complete with confirmed paired release')
import math
events = report.get('events', [])
expected = [('W', True), ('W', False), ('Tab', True), ('Tab', False), ('Tab', True), ('Tab', False)]
@@ -935,6 +960,12 @@ def input_exercise_result(result, context, session, observation, display_result)
or not observation.get('movement', {}).get('physical_motion_observed')
or not observation.get('movement', {}).get('released_input_observed')):
refuse('Native movement and inventory open/close proof is incomplete')
if look_requested:
from ordinary_look_evidence import validate_look_evidence
proof['look_proof'] = validate_look_evidence((session/'console.log').read_text(errors='replace'), context['nonce'], report.get('look_proof'), events)
proof['scope'] = 'bounded private relative mouse and keyboard delivery with native view/movement/inventory evidence; no gate, full escape or multiplayer qualification'
if proof['look_proof'].get('verified') is not True:
refuse('Independent native relative-look evidence is incomplete')
proof['verified'] = True
except (OSError, ValueError, TypeError, AttributeError, KeyError) as error:
proof['error'] = str(error)
@@ -1004,13 +1035,13 @@ def run_locked(a, steam, compat, profile, packed=None):
run_id = uuid.uuid4().hex
observation_nonce = run_id if getattr(a, 'observe_player_input', False) else None
if observation_nonce:
command = input_observer_command(command, a.project, packed, observation_nonce)
command = input_observer_command(command, a.project, packed, observation_nonce, getattr(a, 'exercise_relative_look', False))
display_context = None
if getattr(a, 'inspect_headless_display', False):
command, display_context = display_probe_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''))
exercise_context = None
if getattr(a, 'exercise_player_input', False):
command, exercise_context = input_exercise_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''), display_context)
command, exercise_context = input_exercise_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''), display_context, getattr(a, 'exercise_relative_look', False))
silent_sink = 'erri_game_'+run_id
application_id = 'org.escapistsrereintroduced.game.'+run_id
env = os.environ.copy()
@@ -1127,7 +1158,7 @@ def run_locked(a, steam, compat, profile, packed=None):
if exercise_context:
result, exercise_result = input_exercise_result(result, exercise_context, session, observation_result, display_result)
final['input_exercise'] = exercise_result
final['scope'] = 'ordinary game startup with an explicitly requested bounded private keyboard experiment; see input_exercise for sent-input and native-proof results; no full escape, multiplayer or FPS qualification'
final['scope'] = 'ordinary game startup with an explicitly requested bounded private input experiment; see input_exercise for sent-input and native-proof results; no full escape, multiplayer or FPS qualification'
source_bindings = {} if packed else bindings(captured)
summary = {**sealed, **source_bindings, 'started_unix': started, 'result': result,
'session': str(session) if session else None, 'project': a.project,
@@ -1174,6 +1205,7 @@ def main():
parser.add_argument('--inspect-headless-display', action='store_true', help='Read-only ownership inspection within the hidden display; no input sent, loose Core/Chat only')
parser.add_argument('--capture-prison-view', action='store_true', help='Opt-in single genuine local prison screenshot; loose successor source only, no camera or input changes')
parser.add_argument('--observe-player-input', action='store_true', help='Read-only native local-input sampler; loose Core/Chat only, requires --settle >=50; sends no input')
parser.add_argument('--exercise-relative-look', action='store_true', help='Explicit tiny relative right/return mouse experiment before W/Tab; requires --exercise-player-input and native view proof')
parser.add_argument('--exercise-player-input', action='store_true', help='Explicit bounded private W/Tab keyboard experiment; requires observer, display inspection, loose Core/Chat and settle >=50')
parser.add_argument('--timeout', type=int, default=180)
parser.add_argument('--settle', type=int, default=15)