#!/usr/bin/env bash # SSH keys, and the two things this page must never do. # # The rules: # # 1. A private key is never read for its contents and never leaves the # machine's disk. Fingerprints and comments come from the .pub file. # 2. No passphrase passes through this tool. Adding an encrypted key lets # ssh-add prompt through the system's own askpass; collecting one here and # handing it on would be a worse place for it to live, and putting one in # argv would publish it to every process on the machine. # 3. Key paths are confined to ~/.ssh, resolved and compared, so a name cannot # walk out of the directory. # 4. A control that cannot do what it says is not offered. gnome-keyring's # agent lists every key it finds in ~/.ssh, so `ssh-add -d` reports # "Identity removed" and the key is still offered a second later. Measured # on this machine: a plain ssh-agent removes durably, that one does not. # 5. Copying a public key never splices a path into shell source. # # Read-only against the real configuration. Nothing here adds, removes or # rewrites a key, an agent entry, or a known host. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" helper="$repo_dir/config/dot/quickshell/scripts/panama-ssh-keys" service="$repo_dir/config/dot/quickshell/services/SshKeys.qml" page="$repo_dir/config/dot/quickshell/modules/settings/SshKeysPage.qml" fail() { printf 'ssh keys contract: %s\n' "$1" >&2 exit 1 } for path in "$helper" "$service" "$page"; do [[ -r "$path" ]] || fail "missing $path" done [[ -x "$helper" ]] || fail 'panama-ssh-keys is not executable' field() { python3 -c "import json,sys; print(json.load(sys.stdin)$1)"; } state="$("$helper" snapshot)" || fail 'snapshot failed' # ── 1. Private key material never surfaces ────────────────────────────────── # # Checked against the payload rather than the source: whatever the code intends, # what actually reaches the page must not contain key material. printf '%s' "$state" | python3 -c " import json, sys raw = sys.stdin.read() for marker in ('BEGIN OPENSSH PRIVATE KEY', 'BEGIN RSA PRIVATE KEY', 'BEGIN EC PRIVATE KEY'): if marker in raw: raise SystemExit(f'the snapshot contains {marker}') state = json.loads(raw) for key in state['keys']: for name, value in key.items(): if isinstance(value, str) and len(value) > 200: raise SystemExit(f'{name} is long enough to be key material') " || fail 'private key material reached the snapshot' # The helper must never read a private key for its bytes. The one place it opens # one is ssh-keygen -y, which can only ever emit the public half. grep -q 'read_text' "$helper" && ! grep -q 'KNOWN_HOSTS.read_text' "$helper" \ && fail 'something reads a file directly that is not known_hosts' # ── 2. No passphrase anywhere ─────────────────────────────────────────────── grep -qE '\-N["'"'"' ]' "$helper" \ && fail 'ssh-keygen -N appears, which would put a passphrase in argv' grep -qi 'passphrase' "$service" && ! grep -qi 'never\|prompt' "$service" \ && fail 'the service mentions passphrases without saying it does not handle them' # ── 3. Paths are confined ─────────────────────────────────────────────────── grep -q 'def resolve_key' "$helper" || fail 'key paths are not resolved before use' grep -q 'resolved.parent != SSH_DIR' "$helper" \ || fail 'a key path is not compared against the SSH directory, so it could escape' reason="$(printf '%s' "$("$helper" agent-add /etc/hostname)" | field "['error']")" [[ "$reason" == *"not in the SSH directory"* ]] \ || fail "a path outside ~/.ssh was not refused with a reason (got: $reason)" reason="$(printf '%s' "$("$helper" agent-add /home/nonexistent/.ssh/nope)" | field "['error']")" [[ -n "$reason" ]] || fail 'a missing key was accepted' reason="$(printf '%s' "$("$helper" forget-host 'not a host name')" | field "['error']")" [[ "$reason" == *"not a host name"* ]] \ || fail "an invalid host was not refused with a reason (got: $reason)" # ── 4. A control that cannot deliver is not offered ───────────────────────── grep -q 'durableRemoval' "$helper" \ || fail 'the helper does not record whether removal from this agent sticks' kind="$(printf '%s' "$state" | field "['agent'].get('kind','')")" if [[ "$kind" == "gnome-keyring" ]]; then reason="$(printf '%s' "$("$helper" agent-remove "$HOME/.ssh/id_ed25519")" | field "['error']")" [[ "$reason" == *"does not stick"* ]] \ || fail "removal against a keyring agent was not refused with its reason (got: $reason)" grep -q 'does not stick' "$page" \ || fail 'the page does not say that removing a key from this agent has no effect' fi # ── 5. Copying does not build shell source from a path ────────────────────── grep -q 'exec wl-copy < "\$1"' "$service" \ || fail 'the public key copy does not pass its path as an argument' printf 'ssh keys contract: ok\n'