Files
tutti/tools/scripts/windows/connect-simplysign-linux.sh
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

204 lines
7.0 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# SimplySign exposes no supported headless login API. Drive its GUI through an
# isolated Xvfb session and never enable xtrace: this script handles live OTPs.
: "${CERTUM_USER_ID:?CERTUM_USER_ID is required}"
: "${CERTUM_OTP_URI:?CERTUM_OTP_URI is required}"
: "${SS_DIST:?the signer container must define SS_DIST}"
: "${RUNNER_TEMP:?RUNNER_TEMP is required}"
max_attempts="${TSH_CERTUM_LOGIN_MAX_ATTEMPTS:-3}"
launch_seconds="${TSH_CERTUM_LAUNCH_SECONDS:-20}"
retry_delay_seconds="${TSH_CERTUM_LOGIN_RETRY_DELAY_SECONDS:-10}"
case "${max_attempts}" in
''|*[!0-9]*) echo "TSH_CERTUM_LOGIN_MAX_ATTEMPTS must be a positive integer." >&2; exit 1 ;;
esac
(( max_attempts >= 1 )) || { echo "TSH_CERTUM_LOGIN_MAX_ATTEMPTS must be at least 1." >&2; exit 1; }
ss_start="$(find "${SS_DIST}" -name SimplySignDesktop_start -type f -print -quit)"
ss_exe="$(find "${SS_DIST}" -name SimplySignDesktop -type f -print -quit)"
ss_pkcs11="$(find "${SS_DIST}" -name 'SimplySignPKCS*.so' -type f -print -quit)"
if [[ -z "${ss_exe}" || -z "${ss_pkcs11}" ]]; then
echo "SimplySign Desktop or its PKCS#11 module is missing from ${SS_DIST}." >&2
exit 1
fi
export USER="${USER:-root}"
export DISPLAY=:99
export LD_LIBRARY_PATH="${SS_DIST}:${LD_LIBRARY_PATH:-}"
cp "${SS_DIST}/SimplySignDesktop.xml" "${HOME}/" 2>/dev/null || true
mkdir -p "${HOME}/.config" "${HOME}/.fluxbox"
cat > "${HOME}/.config/Unknown Organization.conf" <<'EOF'
[General]
CacheUserIdAtLogon=Yes
ShowLogonDialogAfterApplicationStartup=Yes
ShowLogonDialogWhenAnyAppRequestsAccess=Yes
EOF
printf 'session.screen0.rootCommand: /bin/true\n' > "${HOME}/.fluxbox/init"
xvfb_pid=''
for display_attempt in $(seq 1 "${max_attempts}"); do
echo "Starting Xvfb display attempt ${display_attempt}/${max_attempts}."
Xvfb :99 -screen 0 1280x1024x24 >"${RUNNER_TEMP}/xvfb-attempt-${display_attempt}.log" 2>&1 &
xvfb_pid=$!
sleep 3
if xdpyinfo >/dev/null 2>&1; then break; fi
kill "${xvfb_pid}" 2>/dev/null || true
wait "${xvfb_pid}" 2>/dev/null || true
xvfb_pid=''
if (( display_attempt < max_attempts )); then sleep "${retry_delay_seconds}"; fi
done
if [[ -z "${xvfb_pid}" ]] || ! xdpyinfo >/dev/null 2>&1; then
echo "Xvfb display failed after ${max_attempts} attempts." >&2
exit 1
fi
fluxbox >"${RUNNER_TEMP}/fluxbox.log" 2>&1 &
fluxbox_pid=$!
sleep 2
xdpyinfo >/dev/null
cleanup_failed_session() {
pkill -TERM -f '[S]implySignDesktop' 2>/dev/null || true
sleep 2
pkill -KILL -f '[S]implySignDesktop' 2>/dev/null || true
}
cleanup_display_on_exit() {
status=$?
trap - EXIT
if (( status != 0 )); then
cleanup_failed_session
kill "${fluxbox_pid}" "${xvfb_pid}" 2>/dev/null || true
fi
exit "${status}"
}
trap cleanup_display_on_exit EXIT
otp_helper="${RUNNER_TEMP}/certum-otp.py"
cat > "${otp_helper}" <<'PY'
import base64
import hashlib
import hmac
import os
import struct
import time
from urllib.parse import parse_qs, urlparse
query = parse_qs(urlparse(os.environ["CERTUM_OTP_URI"]).query)
secret = query["secret"][0]
digits = int(query.get("digits", ["6"])[0])
period = int(query.get("period", ["30"])[0])
algorithm = {"SHA1": hashlib.sha1, "SHA256": hashlib.sha256, "SHA512": hashlib.sha512}[query.get("algorithm", ["SHA1"])[0].upper()]
remaining = period - (time.time() % period)
if remaining < 12:
time.sleep(remaining + 0.5)
key = base64.b32decode(secret + "=" * ((8 - len(secret) % 8) % 8))
counter = struct.pack(">Q", int(time.time() // period))
digest = hmac.new(key, counter, algorithm).digest()
offset = digest[-1] & 0x0F
value = (int.from_bytes(digest[offset : offset + 4], "big") & 0x7FFFFFFF) % (10**digits)
print(str(value).zfill(digits))
PY
wid=''; bx=0; by=0; bw=0; bh=0
find_simplysign_window() {
local candidate name area best=0
wid=''; bx=0; by=0; bw=0; bh=0
while read -r candidate; do
[[ -n "${candidate}" ]] || continue
name="$(xdotool getwindowname "${candidate}" 2>/dev/null || true)"
case "${name}" in *implySign*) ;; *) continue ;; esac
eval "$(xdotool getwindowgeometry --shell "${candidate}" 2>/dev/null || true)"
area=$(( ${WIDTH:-0} * ${HEIGHT:-0} ))
if (( area > best )); then
best=${area}; wid=${candidate}; bx=${X:-0}; by=${Y:-0}; bw=${WIDTH:-0}; bh=${HEIGHT:-0}
fi
done < <(xdotool search --name '' 2>/dev/null || true)
}
pkcs11_config="${RUNNER_TEMP}/certum-sunpkcs11.conf"
{
echo 'name = SimplySign'
echo "library = ${ss_pkcs11}"
echo 'slotListIndex = 0'
} > "${pkcs11_config}"
certificate_visible() {
timeout 30 keytool -list -keystore NONE -storetype PKCS11 \
-providerClass sun.security.pkcs11.SunPKCS11 \
-providerArg "${pkcs11_config}" -storepass '' 2>/dev/null | grep -q PrivateKeyEntry
}
drive_login_form() {
local otp
find_simplysign_window
[[ -n "${wid}" && "${bw}" -ge 400 && "${bh}" -ge 300 ]] || return 1
xdotool windowactivate --sync "${wid}" || xdotool windowactivate "${wid}" || true
xdotool windowraise "${wid}" || true
sleep 1
otp="$(python3 "${otp_helper}")"
echo "::add-mask::${otp}"
xdotool mousemove $((bx + bw / 2)) $((by + bh * 39 / 100)) click 1
xdotool key --clearmodifiers ctrl+a
xdotool key --clearmodifiers Delete
xdotool type --clearmodifiers --delay 50 "${CERTUM_USER_ID}"
xdotool key Tab
xdotool key --clearmodifiers ctrl+a
xdotool key --clearmodifiers Delete
xdotool type --clearmodifiers --delay 50 "${otp}"
xdotool mousemove $((bx + bw / 2)) $((by + bh * 76 / 100)) click 1
unset otp
sleep 8
find_simplysign_window
[[ -n "${wid}" ]] || return 1
xdotool windowactivate --sync "${wid}" || true
xdotool mousemove $((bx + bw / 2)) $((by + bh * 94 / 100)) click 1
sleep 3
for poll in $(seq 1 4); do
if certificate_visible; then
echo "SimplySign certificate is available (poll ${poll})."
return 0
fi
echo "SimplySign certificate is not available yet (poll ${poll}/4)."
sleep 3
done
return 1
}
connected=false
for attempt in $(seq 1 "${max_attempts}"); do
cleanup_failed_session
attempt_log="${RUNNER_TEMP}/simplysign-attempt-${attempt}.log"
echo "Starting SimplySign login attempt ${attempt}/${max_attempts}."
if [[ -n "${ss_start}" ]]; then
"${ss_start}" >"${attempt_log}" 2>&1 &
else
"${ss_exe}" >"${attempt_log}" 2>&1 &
fi
sleep "${launch_seconds}"
for window_poll in $(seq 1 30); do
find_simplysign_window
if [[ -n "${wid}" && "${bw}" -ge 400 && "${bh}" -ge 300 ]]; then
echo "SimplySign login window is ready (attempt ${attempt}, poll ${window_poll})."
break
fi
sleep 2
done
if [[ -n "${wid}" ]] && drive_login_form; then connected=true; break; fi
echo "SimplySign login attempt ${attempt}/${max_attempts} failed." >&2
tail -80 "${attempt_log}" 2>/dev/null || true
if (( attempt < max_attempts )); then sleep "${retry_delay_seconds}"; fi
done
if [[ "${connected}" != true ]]; then
echo "SimplySign login failed after ${max_attempts} attempts." >&2
exit 1
fi
{
echo "DISPLAY=${DISPLAY}"
echo "SS_PKCS11=${ss_pkcs11}"
echo "TSH_CERTUM_PKCS11_CONFIG=${pkcs11_config}"
} >> "${GITHUB_ENV}"
trap - EXIT
echo "SimplySign is connected and ready for Authenticode signing."