263 lines
8 KiB
Bash
Executable file
263 lines
8 KiB
Bash
Executable file
#!/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 <bundle-or-dmg> [options]
|
|
|
|
Options:
|
|
--entitlements <plist> XML entitlements for bundle signing
|
|
--certificate <cer> Apple public certificate matching the KMS key
|
|
--kms-key <name> Google KMS crypto key name/resource
|
|
--kms-version <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[@]}"
|