#!/usr/bin/env bash set -euo pipefail repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$repo_root" family="" path="" entitlements="" certificate="" kms_key="" kms_version="" runtime=false timestamp_url="" usage() { cat <<'EOF' Usage: Scripts/apple/sign-with-google-kms.sh --family ios|developer-id --path [options] Options: --entitlements XML entitlements for bundle signing --certificate Apple public certificate matching the KMS key --kms-key Google KMS crypto key name/resource --kms-version Google KMS crypto key version --runtime Set hardened-runtime code-signature flags EOF } while [[ $# -gt 0 ]]; do case "$1" in --family) family="${2:?missing value for --family}" shift 2 ;; --path) path="${2:?missing value for --path}" shift 2 ;; --entitlements) entitlements="${2:?missing value for --entitlements}" shift 2 ;; --certificate) certificate="${2:?missing value for --certificate}" shift 2 ;; --kms-key) kms_key="${2:?missing value for --kms-key}" shift 2 ;; --kms-version) kms_version="${2:?missing value for --kms-version}" shift 2 ;; --runtime) runtime=true shift ;; -h|--help) usage exit 0 ;; *) echo "error: unknown argument $1" >&2 usage >&2 exit 64 ;; esac done case "$family" in ios) certificate="${certificate:-Apple/Certificates/ios-distribution-3G42677598.cer}" kms_key="${kms_key:-${BURROW_IOS_DISTRIBUTION_KMS_KEY:-apple-ios-distribution}}" kms_version="${kms_version:-${BURROW_IOS_DISTRIBUTION_KMS_VERSION:-1}}" timestamp_url="${BURROW_IOS_CODESIGN_TIMESTAMP_URL:-none}" ;; developer-id) certificate="${certificate:-Apple/Certificates/developer-id-application-9JKN6HXBHC.cer}" kms_key="${kms_key:-${BURROW_DEVELOPER_ID_KMS_KEY:-apple-developer-id-application}}" kms_version="${kms_version:-${BURROW_DEVELOPER_ID_KMS_VERSION:-1}}" timestamp_url="${BURROW_DEVELOPER_ID_TIMESTAMP_URL:-${BURROW_APPLE_TIMESTAMP_URL:-}}" ;; *) echo "error: --family must be ios or developer-id" >&2 exit 64 ;; esac if [[ -z "$path" ]]; then echo "error: --path is required" >&2 exit 64 fi if [[ "$path" != /* ]]; then path="$repo_root/$path" fi if [[ "$certificate" != /* ]]; then certificate="$repo_root/$certificate" fi if [[ -n "$entitlements" && "$entitlements" != /* ]]; then entitlements="$repo_root/$entitlements" fi if [[ ! -e "$path" ]]; then echo "error: signing target does not exist: $path" >&2 exit 1 fi if [[ ! -s "$certificate" ]]; then echo "error: Apple certificate is missing or empty: $certificate" >&2 exit 1 fi if [[ -n "$entitlements" && ! -s "$entitlements" ]]; then echo "error: entitlements file is missing or empty: $entitlements" >&2 exit 1 fi require_tool() { if ! command -v "$1" >/dev/null 2>&1; then echo "error: missing required tool: $1" >&2 exit 1 fi } resolve_module() { local candidate for candidate in \ "${BURROW_APPLE_PKCS11_MODULE:-}" \ "${BURROW_GCP_KMS_PKCS11_MODULE:-}" \ "${BURROW_PKCS11_MODULE:-}" \ "$(command -v google-kms-pkcs11-module >/dev/null 2>&1 && google-kms-pkcs11-module || true)" \ /nix/store/*-google-kms-pkcs11-*/lib/libkmsp11.so \ /nix/store/*-google-kms-pkcs11-*/lib/libkmsp11.dylib; do if [[ -n "$candidate" && -f "$candidate" ]]; then printf '%s\n' "$candidate" return 0 fi done return 1 } cert_public_key() { local cert_file="$1" local out="$2" if ! openssl x509 -inform DER -in "$cert_file" -pubkey -noout >"$out" 2>/dev/null; then openssl x509 -in "$cert_file" -pubkey -noout >"$out" fi } public_fingerprint() { openssl pkey -pubin -in "$1" -outform DER \ | openssl dgst -sha256 -binary \ | od -An -tx1 \ | tr -d ' \n' } resolve_kms_parts() { local resource="$1" local key_ring="${BURROW_GCP_KMS_KEY_RING:-${GOOGLE_KMS_KEY_RING:-projects/${GOOGLE_CLOUD_PROJECT:-project-88c23ce9-918a-470a-b33}/locations/${BURROW_KMS_LOCATION:-global}/keyRings/${BURROW_KMS_KEYRING:-burrow-identity}}}" if [[ "$resource" =~ ^projects/([^/]+)/locations/([^/]+)/keyRings/([^/]+)/cryptoKeys/([^/]+)/cryptoKeyVersions/([^/]+)$ ]]; then printf '%s\n%s\n%s\n%s\n%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "${BASH_REMATCH[4]}" "${BASH_REMATCH[5]}" return 0 fi if [[ "$resource" =~ ^projects/([^/]+)/locations/([^/]+)/keyRings/([^/]+)/cryptoKeys/([^/]+)$ ]]; then printf '%s\n%s\n%s\n%s\n%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "${BASH_REMATCH[4]}" "$kms_version" return 0 fi if [[ ! "$key_ring" =~ ^projects/([^/]+)/locations/([^/]+)/keyRings/([^/]+)$ ]]; then echo "error: invalid KMS key ring resource: $key_ring" >&2 exit 1 fi printf '%s\n%s\n%s\n%s\n%s\n' "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "$resource" "$kms_version" } verify_certificate_matches_kms() { local work_dir="$1" local cert_public="$work_dir/apple-public.pem" local kms_public="$work_dir/kms-public.pem" local project location keyring key version expected actual kms_info="$(resolve_kms_parts "$kms_key")" project="$(printf '%s\n' "$kms_info" | sed -n '1p')" location="$(printf '%s\n' "$kms_info" | sed -n '2p')" keyring="$(printf '%s\n' "$kms_info" | sed -n '3p')" key="$(printf '%s\n' "$kms_info" | sed -n '4p')" version="$(printf '%s\n' "$kms_info" | sed -n '5p')" if [[ -z "$version" ]]; then echo "error: KMS key version is required for Apple release signing" >&2 exit 1 fi cert_public_key "$certificate" "$cert_public" expected="$(public_fingerprint "$cert_public")" gcloud kms keys versions get-public-key "$version" \ --project="$project" \ --location="$location" \ --keyring="$keyring" \ --key="$key" \ --output-file="$kms_public" >/dev/null actual="$(public_fingerprint "$kms_public")" if [[ "$actual" != "$expected" ]]; then echo "error: Apple certificate public key does not match Google KMS key" >&2 echo "family=$family" >&2 echo "kms_key=$key" >&2 echo "kms_version=$version" >&2 echo "certificate_spki_sha256=$expected" >&2 echo "kms_spki_sha256=$actual" >&2 exit 1 fi } require_tool rcodesign require_tool openssl require_tool od require_tool gcloud if ! rcodesign sign --help 2>&1 | grep -Fq -- "--pkcs11-library"; then echo "error: rcodesign does not include PKCS#11 signing support" >&2 exit 1 fi module="$(resolve_module || true)" if [[ -z "$module" ]]; then echo "error: unable to locate Google KMS PKCS#11 module" >&2 exit 1 fi work_dir="$(mktemp -d "${TMPDIR:-/tmp}/burrow-apple-kms-sign.XXXXXX")" trap 'rm -rf "$work_dir"' EXIT eval "$(Scripts/release/google-kms-pkcs11-materialize.sh --emit-env)" verify_certificate_matches_kms "$work_dir" real_openssl="$(command -v openssl)" export BURROW_REAL_OPENSSL="${BURROW_REAL_OPENSSL:-$real_openssl}" export BURROW_OPENSSL="$repo_root/Scripts/release/google-kms-openssl-dgst-shim.sh" export BURROW_APPLE_PKCS11_BACKEND=gcp-kms export BURROW_APPLE_PKCS11_SIGN_WITH_OPENSSL_PROVIDER=true export BURROW_APPLE_PKCS11_SIGN_WITH_GCLOUD=true export BURROW_GCP_KMS_ACTIVE_CRYPTO_KEY="$kms_key" export BURROW_GCP_KMS_ACTIVE_CRYPTO_KEY_VERSION="$kms_version" pin="${BURROW_APPLE_PKCS11_PIN:-${BURROW_GCP_KMS_PKCS11_PIN:-${BURROW_PKCS11_PIN:-ignored}}}" token_label="${BURROW_APPLE_PKCS11_TOKEN_LABEL:-${BURROW_GCP_KMS_PKCS11_TOKEN_LABEL:-${BURROW_PKCS11_TOKEN_LABEL:-burrow-release}}}" args=( sign --pkcs11-library "$module" --pkcs11-certificate-file "$certificate" --pkcs11-token-label "$token_label" --pkcs11-key-label "$kms_key" --pkcs11-pin "$pin" ) if [[ -n "$timestamp_url" ]]; then args+=(--timestamp-url "$timestamp_url") fi if [[ -n "$entitlements" ]]; then args+=(--entitlements-xml-file "$entitlements") fi if [[ "$runtime" == "true" ]]; then args+=(--code-signature-flags runtime) fi args+=("$path") rcodesign "${args[@]}"