From f04736fffd72510c8f4b9d0fc8209567967bd914 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Kai=20L=C3=BCke?= Date: Wed, 29 Apr 2026 15:05:04 +0900 Subject: [PATCH] test: Add sysupdate gpg verification test The test first checks that simple signatures work and that a foreign signature is rejected. Then it also tests that sub key signatures are accepted when the public sub key is included in the signature. --- test/units/TEST-72-SYSUPDATE.sh | 111 ++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/test/units/TEST-72-SYSUPDATE.sh b/test/units/TEST-72-SYSUPDATE.sh index a8bc7432c18..88d7143906c 100755 --- a/test/units/TEST-72-SYSUPDATE.sh +++ b/test/units/TEST-72-SYSUPDATE.sh @@ -56,6 +56,9 @@ EOF systemctl daemon-reload +SIGTEST_GPGHOME= +SIGTEST_OTHERHOME= + at_exit() { set +e @@ -67,6 +70,13 @@ at_exit() { losetup --detach "$loop_dev" done + if [ "$SIGTEST_GPGHOME" != "" ]; then + gpgconf --homedir "$SIGTEST_GPGHOME" --kill all 2>/dev/null + fi + if [ "$SIGTEST_OTHERHOME" != "" ]; then + gpgconf --homedir "$SIGTEST_OTHERHOME" --kill all 2>/dev/null + fi + rm -rf "$WORKDIR" } @@ -1081,4 +1091,105 @@ rm -rf "$CONFIGDIR" "$WORKDIR/blobs" rm -f "$WORKDIR/source/notifytest-v1.bin" "$WORKDIR/source/SHA256SUMS" \ "$WORKDIR/notify-recorder.py" "$NOTIFY_LOG" +test_signature_verification() { + if ! command -v gpg >/dev/null; then + echo "gpg not available, skipping signature verification test" + return 0 + fi + + # Checking for --auto-key-import is not enough because the merge-only/import-clean guarantee we rely on + # only works correctly with gpg 2.4 + local gpg_version gpg_rest + gpg_version="$(gpg --version | sed -n '1p' | awk '{print $NF}')" + gpg_rest="${gpg_version#*.}" + if [ "${gpg_version%%.*}" -lt 2 ] || { [ "${gpg_version%%.*}" -eq 2 ] && [ "${gpg_rest%%.*}" -lt 4 ]; }; then + echo "gpg $gpg_version too old (need >= 2.4), skipping signature verification test" + return 0 + fi + + local sigdir="$WORKDIR/sigtest-source" + local defdir="$WORKDIR/sigtest-defs" + local gpghome="$WORKDIR/sigtest-gpghome" + local other_home="$WORKDIR/sigtest-otherhome" + local target="$WORKDIR/sigtest-target" + local keyring="$WORKDIR/sigtest-keyring" + local top_fpr keys + + SIGTEST_GPGHOME="$gpghome" + SIGTEST_OTHERHOME="$other_home" + + mkdir -p "$sigdir" "$defdir" "$gpghome" "$other_home" "$target" + chmod 700 "$gpghome" "$other_home" + + GNUPGHOME="$gpghome" gpg --batch --pinentry-mode loopback --passphrase '' \ + --quick-gen-key 'Test Key ' rsa2048 cert,sign never + # Capture gpg output first, then grep from a here-string to avoid grep -m1 causing a SIGPIPE + keys="$(GNUPGHOME="$gpghome" gpg --list-keys --with-colons)" + top_fpr="$(grep -m1 '^fpr:' <<< "$keys" | cut -d: -f10)" + test "$top_fpr" != "" + + GNUPGHOME="$gpghome" gpg --export --output "$keyring" + + dd if=/dev/urandom of="$sigdir/payload-v1.raw" bs=1024 count=8 status=none + (cd "$sigdir" && sha256sum payload-v1.raw > SHA256SUMS) + GNUPGHOME="$gpghome" gpg --batch --pinentry-mode loopback --passphrase '' \ + --detach-sign --include-key-block --yes \ + --output "$sigdir/SHA256SUMS.gpg" "$sigdir/SHA256SUMS" + + cat >"$defdir/01-sigtest.transfer" <' rsa2048 cert,sign never + dd if=/dev/urandom of="$sigdir/payload-v2.raw" bs=1024 count=8 status=none + (cd "$sigdir" && sha256sum payload-v1.raw payload-v2.raw > SHA256SUMS) + GNUPGHOME="$other_home" gpg --batch --pinentry-mode loopback --passphrase '' \ + --detach-sign --include-key-block --yes \ + --output "$sigdir/SHA256SUMS.gpg" "$sigdir/SHA256SUMS" + if SYSTEMD_OPENPGP_KEYRING="$keyring" "$SYSUPDATE" --definitions="$defdir" update; then + echo "ERROR: accepted an update signed by a key not in the keyring" >&2 + exit 1 + fi + if [ -f "$target/payload-v2.raw" ]; then + echo "ERROR: payload-v2 should not have been installed" >&2 + exit 1 + fi + + # Sub key test: Add a sub key the client does not have and rely on gpg + # --auto-key-import to get it from the signature. + GNUPGHOME="$gpghome" gpg --batch --pinentry-mode loopback --passphrase '' \ + --quick-add-key "$top_fpr" rsa2048 sign 1y + # Make it so that only the sub key is available for signing to avoid having + # to select it by fingerprint. + GNUPGHOME="$gpghome" gpg --batch --pinentry-mode loopback --passphrase '' \ + --output "$WORKDIR/sigtest-subkey-secret.gpg" \ + --export-secret-subkeys + GNUPGHOME="$gpghome" gpg --batch --pinentry-mode loopback --passphrase '' \ + --yes --delete-secret-keys "$top_fpr" + GNUPGHOME="$gpghome" gpg --batch --pinentry-mode loopback --passphrase '' \ + --import "$WORKDIR/sigtest-subkey-secret.gpg" + GNUPGHOME="$gpghome" gpg --batch --pinentry-mode loopback --passphrase '' \ + --detach-sign --include-key-block --yes \ + --output "$sigdir/SHA256SUMS.gpg" "$sigdir/SHA256SUMS" + SYSTEMD_OPENPGP_KEYRING="$keyring" "$SYSUPDATE" --definitions="$defdir" update + cmp "$sigdir/payload-v2.raw" "$target/payload-v2.raw" +} + +test_signature_verification + touch /testok -- 2.47.3