#!/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