cat / goq
clone

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

fix/website-download-wiring
goq / scripts / verify-macos-portal-signature.sh
verify-macos-portal-signature.sh 4.9K executable
1#!/usr/bin/env bash23set -euo pipefail45die() {6 printf 'Portal signature verification failed: %s\n' "$*" >&27 exit 18}910usage() {11 printf 'Usage: %s --dmg /absolute/path/Portal-VERSION-arm64.dmg --signing adhoc|developer-id [--expected-team-id TEAMID] [--expected-version VERSION]\n' "$0"12}1314dmg=''15expected_version=''16expected_team_id=''17signing_mode=''18while (($#)); do19 case "$1" in20 --dmg) [[ $# -ge 2 ]] || die "$1 requires a value"; dmg="$2"; shift 2 ;;21 --expected-version) [[ $# -ge 2 ]] || die "$1 requires a value"; expected_version="$2"; shift 2 ;;22 --expected-team-id) [[ $# -ge 2 ]] || die "$1 requires a value"; expected_team_id="$2"; shift 2 ;;23 --signing) [[ $# -ge 2 ]] || die "$1 requires a value"; signing_mode="$2"; shift 2 ;;24 --help|-h) usage; exit 0 ;;25 *) die "unknown argument: $1" ;;26 esac27done2829[[ "$(uname -s)" == Darwin ]] || die 'verification must run on macOS'30[[ "$dmg" == /* && -f "$dmg" && ! -L "$dmg" ]] || die 'DMG must be an absolute regular file'31case "$signing_mode" in32 developer-id)33 [[ "$expected_team_id" =~ ^[A-Z0-9]{10}$ ]] \34 || die 'expected Apple TeamIdentifier is required for developer-id mode'35 ;;36 adhoc)37 [[ -z "$expected_team_id" ]] \38 || die '--expected-team-id is meaningless for an ad-hoc release'39 ;;40 '') die '--signing adhoc|developer-id is required; there is no default' ;;41 *) die "unknown signing mode: $signing_mode" ;;42esac43for command_name in codesign find grep hdiutil lipo plutil sed spctl xcrun; do44 command -v "$command_name" >/dev/null 2>&1 || die "$command_name is required"45done4647temporary_base="${TMPDIR:-/tmp}"48verification_root="$(mktemp -d "${temporary_base%/}/goq-portal-verify.XXXXXX")"49mount_path="$verification_root/mount"50mkdir -m 0700 "$mount_path"51mounted=false52cleanup() {53 local status=$?54 trap - EXIT INT TERM HUP55 if $mounted; then56 hdiutil detach "$mount_path" -force >/dev/null 2>&1 || true57 fi58 rmdir "$mount_path" "$verification_root" >/dev/null 2>&1 || true59 exit "$status"60}61trap cleanup EXIT INT TERM HUP6263hdiutil verify "$dmg" >/dev/null64if [[ "$signing_mode" == developer-id ]]; then65 xcrun stapler validate "$dmg"66 spctl --assess --type open --context context:primary-signature --verbose=4 "$dmg"67elif xcrun stapler validate "$dmg" >/dev/null 2>&1; then68 die 'ad-hoc release unexpectedly carries a notarization ticket'69fi70hdiutil attach "$dmg" -readonly -nobrowse -mountpoint "$mount_path" >/dev/null71mounted=true7273app_count="$(find "$mount_path" -maxdepth 2 -type d -name 'Portal.app' -print | wc -l | tr -d ' ')"74[[ "$app_count" == 1 ]] || die 'DMG must contain exactly one Portal.app'75app_path="$(find "$mount_path" -maxdepth 2 -type d -name 'Portal.app' -print)"76codesign --verify --deep --strict --verbose=4 "$app_path"77signature_details="$(codesign -d --verbose=4 "$app_path" 2>&1)"78if [[ "$signing_mode" == developer-id ]]; then79 grep -Eq '^Authority=Developer ID Application:' <<<"$signature_details" \80 || die 'application is not signed by a Developer ID Application identity'81 team_identifier="$(sed -n 's/^TeamIdentifier=//p' <<<"$signature_details")"82 [[ "$team_identifier" == "$expected_team_id" ]] \83 || die 'application TeamIdentifier does not match the pinned Portal signer'84 grep -Eq '^flags=.*runtime' <<<"$signature_details" \85 || die 'application signature does not enable hardened runtime'86 spctl --assess --type execute --verbose=4 "$app_path"87 xcrun stapler validate "$app_path"88else89 # The ad-hoc signature is what lets arm64 macOS run the binary at all. It90 # asserts nothing about origin, so also prove no Apple identity crept in.91 grep -Fq 'Signature=adhoc' <<<"$signature_details" \92 || die 'ad-hoc release must carry the mandatory arm64 ad-hoc signature'93 if grep -q '^Authority=' <<<"$signature_details"; then94 die 'ad-hoc release unexpectedly carries a certificate authority chain'95 fi96 if grep -q '^TeamIdentifier=[A-Z0-9]' <<<"$signature_details"; then97 die 'ad-hoc release unexpectedly carries an Apple TeamIdentifier'98 fi99fi100101identifier="$(plutil -extract CFBundleIdentifier raw "$app_path/Contents/Info.plist")"102[[ "$identifier" == sh.goq.portal ]] || die "unexpected bundle identifier: $identifier"103version="$(plutil -extract CFBundleShortVersionString raw "$app_path/Contents/Info.plist")"104[[ -z "$expected_version" || "$version" == "$expected_version" ]] \105 || die "app version $version does not match $expected_version"106executable="$app_path/Contents/MacOS/portal"107[[ -x "$executable" ]] || die 'Portal executable is missing'108[[ "$(lipo -archs "$executable")" == arm64 ]] || die 'Portal executable is not arm64-only'109if find "$app_path" -type f \( -name sigil -o -name sigil-host -o -name sigil-probe \110 -o -name host.toml -o -name '*.key' \) | grep -q .111then112 die 'Portal contains a forbidden host or credential artifact'113fi114115printf 'portal_signature_verification=ok\nportal_version=%s\nportal_architecture=arm64\nportal_signing=%s\n' \116 "$version" "$signing_mode"