cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/portal-dist-prebuild
goq / scripts / tests / authenticated-probe-proof.sh
authenticated-probe-proof.sh 9.6K executable
1#!/usr/bin/env bash23set -euo pipefail45script_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"6repo_dir="$(CDPATH='' cd -- "$script_dir/../.." && pwd -P)"7temp_parent="${TMPDIR:-/tmp}"8temp_parent="${temp_parent%/}"9# Darwin's per-user TMPDIR is long enough to overflow sockaddr_un once the10# owner-only authorization socket is appended. Use the same private mktemp11# directory under the short system alias; the generated child remains mode 0700.12if [[ "$(uname -s)" == Darwin ]]; then13 temp_parent=/tmp14fi15temp_root="$(mktemp -d "$temp_parent/sigil-auth-probe.XXXXXX")"1617host_pid=""18cleanup() {19 local status=$?20 trap - EXIT INT TERM HUP21 if [[ -n "$host_pid" ]] && kill -0 "$host_pid" 2>/dev/null; then22 kill -TERM "$host_pid" 2>/dev/null || true23 wait "$host_pid" 2>/dev/null || true24 fi25 case "$temp_root" in26 "$temp_parent"/sigil-auth-probe.??????) rm -rf -- "$temp_root" ;;27 esac28 exit "$status"29}30trap cleanup EXIT INT TERM HUP3132die() {33 printf 'authenticated probe proof failed: %s\n' "$*" >&234 exit 135}3637wait_for_ready() {38 local deadline=$((SECONDS + 30))39 while (( SECONDS < deadline )); do40 grep -Fxq 'status=ready' "$host_log" 2>/dev/null && return 041 kill -0 "$host_pid" 2>/dev/null || return 142 sleep 0.143 done44 return 145}4647wait_for_log_count() {48 local literal="$1"49 local expected="$2"50 local deadline=$((SECONDS + 15))51 local observed52 while (( SECONDS < deadline )); do53 observed="$(grep -Fc -- "$literal" "$host_log" 2>/dev/null || true)"54 [[ "$observed" -ge "$expected" ]] && return 055 kill -0 "$host_pid" 2>/dev/null || return 156 sleep 0.157 done58 return 159}6061cd "$repo_dir"62if [[ -f "${HOME}/.cargo/env" ]]; then63 # shellcheck source=/dev/null64 source "${HOME}/.cargo/env"65fi66cargo build --locked -p sigil-host --bins6768target_root="${CARGO_TARGET_DIR:-$repo_dir/target}"69case "$target_root" in70 /*) ;;71 *) target_root="$repo_dir/$target_root" ;;72esac73host_bin="$target_root/debug/sigil"74probe_bin="$target_root/debug/sigil-probe"75ffmpeg_bin="$(command -v ffmpeg)"76[[ -x "$host_bin" && -x "$probe_bin" ]] || die 'host/probe binaries are missing'77[[ -x "$ffmpeg_bin" ]] || die 'ffmpeg is unavailable'7879export XDG_RUNTIME_DIR="$temp_root/xdg"80install -d -m 0700 "$XDG_RUNTIME_DIR"81host_identity="$temp_root/host.key"82probe_identity="$temp_root/probe.key"83host_config="$temp_root/host.toml"84invitation="$temp_root/probe.goq-invite"85host_log="$temp_root/host.log"86first_log="$temp_root/first.log"87reconnect_log="$temp_root/reconnect.log"88compat_log="$temp_root/compat.log"89replay_log="$temp_root/replay.log"90control_v2_log="$temp_root/control-v2.log"91second_identity="$temp_root/second.key"92second_invitation="$temp_root/second.goq-invite"93second_v2_log="$temp_root/control-v2-second.log"9495"$host_bin" identity init --output "$host_identity" >"$temp_root/host-identity.log"96"$host_bin" identity init --output "$probe_identity" >"$temp_root/probe-identity.log"97"$host_bin" identity init --output "$second_identity" >"$temp_root/second-identity.log"98host_node_id="$(sed -n 's/^node_id=//p' "$temp_root/host-identity.log")"99probe_node_id="$(sed -n 's/^node_id=//p' "$temp_root/probe-identity.log")"100second_node_id="$(sed -n 's/^node_id=//p' "$temp_root/second-identity.log")"101[[ -n "$host_node_id" && -n "$probe_node_id" ]] || die 'identity output is incomplete'102103printf '%s\n' \104 "identity_path = \"$host_identity\"" \105 "state_path = \"$temp_root/state\"" \106 'source = "test-pattern"' \107 'width = 1280' \108 'height = 800' \109 'framerate = 60' \110 'codec = "h264"' \111 'input_mode = "log"' \112 "ffmpeg_path = \"$ffmpeg_bin\"" \113 >"$host_config"114chmod 0600 "$host_config"115"$host_bin" config check --config "$host_config" >/dev/null116117RUST_LOG='info,sigil::server=debug' "$host_bin" serve \118 --config "$host_config" \119 --max-runtime-seconds 90 \120 >"$host_log" 2>&1 &121host_pid=$!122wait_for_ready || {123 sed -n '1,160p' "$host_log" >&2 || true124 die 'configured host did not become ready'125}126"$host_bin" invitation create \127 --config "$host_config" \128 --peer "$probe_node_id" \129 --pointer-keyboard \130 --output "$invitation" \131 >/dev/null132133"$probe_bin" \134 --node-id "$host_node_id" \135 --identity "$probe_identity" \136 --invitation "$invitation" \137 --frames 1 \138 >"$first_log"139wait_for_log_count 'MoQ control client released' 1 \140 || die 'first authenticated session did not drain'141142"$probe_bin" \143 --node-id "$host_node_id" \144 --identity "$probe_identity" \145 --frames 1 \146 >"$reconnect_log"147wait_for_log_count 'MoQ control client released' 2 \148 || die 'ticket-free session did not drain'149150"$probe_bin" \151 --node-id "$host_node_id" \152 --identity "$probe_identity" \153 --media-v3 \154 --frames 1 \155 >"$compat_log"156wait_for_log_count 'media v3 client released' 1 \157 || die 'grouped-v3 session did not drain'158159# Enrolled control-v2 coverage. Every other control-v2 proof runs the host160# without a config, which selects TestPatternProof and pins161# authorization_revision to 1. A configured host mints each viewer's162# subscription capability at that viewer's real enrollment revision, which163# starts at 2 and climbs with every enrollment and grant change. Without this164# case a client that assumes revision 1 passes the whole gate and then rejects165# its own authenticated media on the appliance.166"$probe_bin" \167 --node-id "$host_node_id" \168 --identity "$probe_identity" \169 --control-v2 \170 --frames 4 \171 >"$control_v2_log"172wait_for_log_count 'MoQ control v2 client released' 1 \173 || die 'enrolled control-v2 session did not drain'174175# A second enrolled viewer lands on a strictly higher revision, so a capability176# check that is right for the first viewer can still be wrong for this one.177"$host_bin" invitation create \178 --config "$host_config" \179 --peer "$second_node_id" \180 --pointer-keyboard \181 --output "$second_invitation" \182 >/dev/null183"$probe_bin" \184 --node-id "$host_node_id" \185 --identity "$second_identity" \186 --invitation "$second_invitation" \187 --control-v2 \188 --spectator \189 --frames 4 \190 >"$second_v2_log"191wait_for_log_count 'MoQ control v2 client released' 2 \192 || die 'second enrolled control-v2 session did not drain'193194for v2_log in "$control_v2_log" "$second_v2_log"; do195 grep -Fxq 'probe=ok' "$v2_log" || die "control-v2 evidence is missing from $v2_log"196 grep -Fxq 'control_alpn=sigil/control/2' "$v2_log" \197 || die "control-v2 ALPN is missing from $v2_log"198done199grep -Fxq 'media_authentication=ed25519-v1' "$control_v2_log" \200 || die 'enrolled control-v2 media was not authenticated'201grep -Fxq 'media_generation_certified=ok' "$control_v2_log" \202 || die 'enrolled control-v2 generation certificate was not verified'203204# Pin the property that made the shipped defect invisible: these revisions are205# not the development-bypass constant.206# The subscriber writes ANSI field separators, so strip them before matching.207ansi_escape=$'\033'208enrolled_revisions="$(grep -F 'MoQ control v2 client accepted' "$host_log" \209 | sed -e "s/${ansi_escape}\[[0-9;]*m//g" \210 | sed -n 's/.* authorization_revision=\([0-9][0-9]*\) .*/\1/p')"211if [[ -z "$enrolled_revisions" ]]; then212 grep -F 'control v2 client accepted' "$host_log" >&2 || true213 die 'host did not record an authorization revision for control-v2 admission'214fi215observed_revisions=0216while read -r revision; do217 [[ -n "$revision" ]] || continue218 observed_revisions=$((observed_revisions + 1))219 [[ "$revision" -ge 2 ]] \220 || die "enrolled control-v2 admission used revision $revision, so this proof could not \221detect a client that assumes the development-bypass constant"222done <<<"$enrolled_revisions"223[[ "$observed_revisions" -eq 2 ]] \224 || die "expected two enrolled control-v2 admissions, saw $observed_revisions"225[[ "$(sort -u <<<"$enrolled_revisions" | wc -l | tr -d ' ')" -eq 2 ]] \226 || die 'the two enrolled viewers did not land on distinct authorization revisions'227228for proof_log in "$first_log" "$reconnect_log" "$compat_log"; do229 grep -Fxq 'probe=ok' "$proof_log" || die "probe evidence is missing from $proof_log"230 grep -Fxq 'sequence_gaps=0' "$proof_log" || die "sequence gap in $proof_log"231 awk -F= '$1 == "input_ack_micros" && $2 ~ /^[0-9]+$/ { found=1 } END { exit !found }' \232 "$proof_log" || die "input acknowledgment is missing from $proof_log"233done234grep -Fxq 'transport=iroh-moq' "$first_log" || die 'first transport is not upstream MoQ'235grep -Fxq 'transport=iroh-moq' "$reconnect_log" || die 'reconnect transport is not upstream MoQ'236grep -Fxq 'transport=grouped-v3' "$compat_log" || die 'compatibility transport is not grouped-v3'237238if "$probe_bin" \239 --node-id "$host_node_id" \240 --identity "$probe_identity" \241 --invitation "$invitation" \242 --frames 4 \243 >"$replay_log" 2>&1; then244 die 'redeemed invitation replay was accepted'245fi246grep -Fq 'host rejected control stream: Portal peer is not authorized' "$replay_log" \247 || {248 sed -n '1,80p' "$replay_log" >&2 || true249 die 'redeemed invitation replay returned the wrong rejection'250 }251enrollment="$($host_bin enrollment show --config "$host_config")"252grep -Fxq 'enrollment=active' <<<"$enrollment" || die 'probe enrollment is not active'253grep -Fxq 'viewer_count=2' <<<"$enrollment" || die 'probe enrollment count changed'254grep -Eq '^viewer=viewer-[0-9a-f]{16} grants=view,pointer-keyboard authorization_revision=[1-9][0-9]* enrolled_at_unix=[0-9]+$' \255 <<<"$enrollment" || die 'opaque enrollment handle or grants changed'256if grep -Fq "$probe_node_id" <<<"$enrollment"; then257 die 'enrollment inspection exposed the raw peer identity'258fi259260printf 'authenticated_probe_proof=ok\n'261printf 'invitation_redemption=ok\n'262printf 'ticket_free_reconnect=ok\n'263printf 'grouped_v3_reconnect=ok\n'264printf 'invitation_replay=rejected\n'265printf 'enrolled_control_v2=ok\n'266printf 'enrolled_multi_viewer_revisions=ok\n'