diff --git a/scripts/ci/setup-python.sh b/scripts/ci/setup-python.sh index 64f5e09..63bdf04 100755 --- a/scripts/ci/setup-python.sh +++ b/scripts/ci/setup-python.sh @@ -1,6 +1,7 @@ #!/bin/sh -# Build and install Python from official python.org source with GPG verification. -# Source: https://www.python.org +# Build and install Python from official python.org source. +# Verifies: OpenPGP (.asc) when present, otherwise Sigstore (.sigstore) with cosign. +# https://www.python.org/download/sigstore/ PEP 761 (3.14+): PGP removed; Sigstore only. # # Usage: setup-python.sh [version] # version: exact (3.14.x) or minor (3.14, resolved to latest patch). @@ -8,6 +9,42 @@ set -eu . "$(dirname "$0")/priv.sh" +# Sigstore identity and OIDC issuer per release (see python.org/download/sigstore). +sigstore_identity_for() { + _v="$1" + case "$_v" in + 3.7.*) echo "nad@python.org|https://github.com/login/oauth" ;; + 3.8.*|3.9.*) echo "lukasz@langa.pl|https://github.com/login/oauth" ;; + 3.10.*|3.11.*) echo "pablogsal@python.org|https://accounts.google.com" ;; + 3.12.*|3.13.*) echo "thomas@python.org|https://accounts.google.com" ;; + 3.14.*|3.15.*) echo "hugo@python.org|https://github.com/login/oauth" ;; + 3.16.*|3.17.*) echo "savannah@python.org|https://github.com/login/oauth" ;; + 3.*.*) echo "savannah@python.org|https://github.com/login/oauth" ;; + *) echo "" ;; + esac +} + +download_cosign() { + COSIGN_VERSION="${COSIGN_VERSION:-2.4.1}" + COSIGN_ARCH="linux-amd64" + case "$(uname -m)" in + aarch64|arm64) COSIGN_ARCH="linux-arm64" ;; + x86_64|amd64) COSIGN_ARCH="linux-amd64" ;; + x86_64-gnu) COSIGN_ARCH="linux-amd64" ;; + *) + echo "unsupported uname -m for cosign: $(uname -m)" >&2 + exit 1 + ;; + esac + COSIGN_BIN="/tmp/cosign-${COSIGN_VERSION}-${COSIGN_ARCH}" + if [ ! -x "$COSIGN_BIN" ]; then + curl -fsSL "https://github.com/sigstore/cosign/releases/download/v${COSIGN_VERSION}/cosign-${COSIGN_ARCH}" \ + -o "$COSIGN_BIN" + chmod +x "$COSIGN_BIN" + fi + echo "$COSIGN_BIN" +} + PY_INPUT="${1:-3.14}" CURRENT="$(python3 --version 2>/dev/null | sed 's/Python //')" || true @@ -50,7 +87,11 @@ SRC_URL="https://www.python.org/ftp/python/${PY_VERSION}/${TARBALL}" SIG_URL="${SRC_URL}.asc" curl -fsSL "$SRC_URL" -o "/tmp/${TARBALL}" -curl -fsSL "$SIG_URL" -o "/tmp/${TARBALL}.asc" + +SIG_HTTP="$(curl -sS -o "/tmp/${TARBALL}.asc" -w "%{http_code}" "$SIG_URL" || true)" +if [ "$SIG_HTTP" != "200" ]; then + rm -f "/tmp/${TARBALL}.asc" +fi GPG_KEYS="" case "$PY_VERSION" in @@ -64,11 +105,12 @@ case "$PY_VERSION" in GPG_KEYS="E3FF2839C048B25C084DEBE9B26995E310250568" ;; *) - echo "No known GPG key for Python ${PY_VERSION}; skipping signature check" >&2 + echo "No known GPG key for Python ${PY_VERSION}; skipping OpenPGP signature check" >&2 ;; esac -if [ -n "$GPG_KEYS" ]; then +GPG_VERIFIED=0 +if [ "$SIG_HTTP" = "200" ] && [ -n "$GPG_KEYS" ]; then export GNUPGHOME="$(mktemp -d)" for key in $GPG_KEYS; do gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" 2>/dev/null || \ @@ -77,7 +119,32 @@ if [ -n "$GPG_KEYS" ]; then gpg --batch --verify "/tmp/${TARBALL}.asc" "/tmp/${TARBALL}" rm -rf "$GNUPGHOME" unset GNUPGHOME - echo "GPG signature verified" + echo "OpenPGP signature verified" + GPG_VERIFIED=1 +fi + +if [ "$GPG_VERIFIED" != "1" ]; then + SIGSTORE_URL="${SRC_URL}.sigstore" + SIGSTORE_HTTP="$(curl -sS -o "/tmp/${TARBALL}.sigstore" -w "%{http_code}" "$SIGSTORE_URL" || true)" + if [ "$SIGSTORE_HTTP" != "200" ]; then + rm -f "/tmp/${TARBALL}.sigstore" + echo "No OpenPGP signature (HTTP ${SIG_HTTP}) and no Sigstore bundle at ${SIGSTORE_URL} (HTTP ${SIGSTORE_HTTP})" >&2 + exit 1 + fi + II="$(sigstore_identity_for "$PY_VERSION")" + if [ -z "$II" ]; then + echo "No Sigstore identity mapping for Python ${PY_VERSION}" >&2 + exit 1 + fi + SIG_IDENTITY="$(echo "$II" | cut -d'|' -f1)" + SIG_ISSUER="$(echo "$II" | cut -d'|' -f2)" + COSIGN_BIN="$(download_cosign)" + "$COSIGN_BIN" verify-blob --new-bundle-format \ + --certificate-oidc-issuer "$SIG_ISSUER" \ + --certificate-identity "$SIG_IDENTITY" \ + --bundle "/tmp/${TARBALL}.sigstore" \ + "/tmp/${TARBALL}" + echo "Sigstore signature verified" fi cd /tmp @@ -93,7 +160,7 @@ run_priv ln -sf /usr/local/bin/python3 /usr/local/bin/python run_priv ln -sf /usr/local/bin/pip3 /usr/local/bin/pip cd / -rm -rf "/tmp/${TARBALL}" "/tmp/${TARBALL}.asc" "/tmp/Python-${PY_VERSION}" "$BUILD_LOG" +rm -rf "/tmp/${TARBALL}" "/tmp/${TARBALL}.asc" "/tmp/${TARBALL}.sigstore" "/tmp/Python-${PY_VERSION}" "$BUILD_LOG" python3 --version pip3 --version