88 lines
3.7 KiB
Bash
Executable file
88 lines
3.7 KiB
Bash
Executable file
#!/bin/sh
|
|
# The signer, end to end, against SoftHSM instead of the real HSM.
|
|
#
|
|
# test.sh detached signatures only
|
|
# test.sh UNSIGNED.apk [SIGNED.apk] and APK signing; the second keeps the signed APK
|
|
#
|
|
# Builds the signer image exactly as the action does, adds SoftHSM to it, and makes a
|
|
# token with an RSA key and no certificate, the state the real HSM's key is in. Then it
|
|
# makes a detached signature and checks it with openssl against the token's public key
|
|
# and, given an APK, runs make-cert and apk, and has apksigner verify the result.
|
|
# Everything but pcscd and the hardware is the code that runs in CI. Uses docker, or
|
|
# podman when there is no docker. Scratch space comes from mktemp, so TMPDIR moves it.
|
|
set -eu
|
|
|
|
usage() {
|
|
echo "usage: test.sh [UNSIGNED.apk [SIGNED.apk]]" >&2
|
|
exit 2
|
|
}
|
|
[ $# -le 2 ] || usage
|
|
apk_in=""
|
|
apk_out="${2:-}"
|
|
if [ $# -ge 1 ]; then
|
|
[ -f "$1" ] || usage
|
|
apk_in="$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
|
|
fi
|
|
here="$(cd "$(dirname "$0")" && pwd)"
|
|
engine="$(command -v docker || command -v podman)" || { echo "need docker or podman" >&2; exit 2; }
|
|
|
|
"$engine" build -q -t action-hsm-sign-signer:test "$here" >/dev/null
|
|
"$engine" build -q -t action-hsm-sign-signer:softhsm - >/dev/null <<'EOF'
|
|
FROM action-hsm-sign-signer:test
|
|
USER root
|
|
RUN apt-get update && apt-get install -y --no-install-recommends softhsm2 && rm -rf /var/lib/apt/lists/*
|
|
USER user
|
|
EOF
|
|
|
|
out_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$out_dir"' EXIT
|
|
chmod 777 "$out_dir"
|
|
|
|
set -- -v "$out_dir:/out"
|
|
if [ -n "$apk_in" ]; then
|
|
set -- "$@" -v "$apk_in:/home/user/in.apk:ro"
|
|
fi
|
|
|
|
# shellcheck disable=SC2016 # expanded inside the container
|
|
"$engine" run --rm --entrypoint /bin/sh "$@" action-hsm-sign-signer:softhsm -euc '
|
|
export SOFTHSM2_CONF="$HOME/softhsm2.conf"
|
|
mkdir -p "$HOME/tokens"
|
|
echo "directories.tokendir = $HOME/tokens" > "$SOFTHSM2_CONF"
|
|
export PKCS11_MODULE="$(find /usr/lib -name libsofthsm2.so | head -n 1)"
|
|
export PKCS11_PIN=123456 KEY_ID=03
|
|
softhsm2-util --init-token --free --label hsm-sign-test --pin "$PKCS11_PIN" --so-pin 87654321 >/dev/null
|
|
# A key and nothing else, as on the real HSM before make-cert.
|
|
pkcs11-tool --module "$PKCS11_MODULE" --login --pin env:PKCS11_PIN \
|
|
--keypairgen --key-type rsa:4096 --id "$KEY_ID" --label test-key >/dev/null
|
|
pkcs11-tool --module "$PKCS11_MODULE" --read-object --type pubkey --id "$KEY_ID" |
|
|
openssl pkey -pubin -inform DER -out "$HOME/pub.pem"
|
|
want="$(openssl pkey -pubin -in "$HOME/pub.pem" -outform DER | sha256sum | cut -d" " -f1)"
|
|
|
|
echo "== detached"
|
|
echo "an artifact" > artifact
|
|
hsm-sign detached artifact /out/artifact.sig
|
|
# What rekor and users do with the published public key.
|
|
openssl dgst -sha256 -verify "$HOME/pub.pem" -signature /out/artifact.sig artifact
|
|
|
|
[ -f in.apk ] || exit 0
|
|
echo "== make-cert"
|
|
hsm-sign make-cert "/CN=hsm-sign test signer" > /out/cert.pem
|
|
openssl x509 -in /out/cert.pem -noout -subject -fingerprint -sha256
|
|
echo "== apk"
|
|
hsm-sign apk in.apk /out/signed.apk
|
|
echo "== independent verify"
|
|
apksigner verify --verbose /out/signed.apk
|
|
# The APK is signed by the key, not merely by a certificate naming it: the public key
|
|
# apksigner reports is the token'"'"'s.
|
|
got="$(apksigner verify --verbose --print-certs /out/signed.apk | sed -n "s/^Signer #1 public key SHA-256 digest: //p")"
|
|
echo "token public key sha256: $want"
|
|
echo "APK signer key sha256: $got"
|
|
[ "$want" = "$got" ]
|
|
'
|
|
echo "test.sh: detached signature verified"
|
|
if [ -n "$apk_in" ]; then
|
|
if [ -n "$apk_out" ]; then
|
|
cp "$out_dir/signed.apk" "$apk_out"
|
|
fi
|
|
echo "test.sh: signed and verified $(basename "$apk_in")"
|
|
fi
|