summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve McIntyre <steve@einval.com>2026-04-28 14:01:32 +0100
committerSteve McIntyre <steve@einval.com>2026-04-28 17:35:25 +0100
commite2a56d673c66989136c41a6f1c997ff1aad10097 (patch)
tree9679eb8f7be520822e397011e6e97ab941b58658
parentd51bfcf945e1070a551cf8ec5d94b91b213991e1 (diff)
downloadshim-signed-e2a56d673c66989136c41a6f1c997ff1aad10097.tar.gz
shim-signed-e2a56d673c66989136c41a6f1c997ff1aad10097.zip
Add support for verifying and then combining signatures
from multiple signed shims. * Move the verification logic out into a new helper script verify_combine_sigs - see comments there for how it works. * Rename the existing shim binaries and CA cert to match * Include some extra certs and binaries for testing with
-rw-r--r--0001-MicCorUEFCA2011_2011-06-27.crt (renamed from MicCorUEFCA2011_2011-06-27.crt)0
-rw-r--r--0002-snakeoil.crt21
-rw-r--r--0003-wrong.crt26
-rw-r--r--Makefile17
-rw-r--r--TEST-shimx64.efi.signed.multibin0 -> 958840 bytes
-rw-r--r--TEST-shimx64.efi.signed.snakeoilbin0 -> 949072 bytes
-rw-r--r--debian/changelog9
-rw-r--r--debian/control3
-rw-r--r--shimaa64.efi.signed.MS-2011 (renamed from shimaa64.efi.signed)bin987432 -> 987432 bytes
-rw-r--r--shimx64.efi.signed.MS-2011 (renamed from shimx64.efi.signed)bin957136 -> 957136 bytes
-rwxr-xr-xverify_combine_sigs294
11 files changed, 355 insertions, 15 deletions
diff --git a/MicCorUEFCA2011_2011-06-27.crt b/0001-MicCorUEFCA2011_2011-06-27.crt
index d7c29ef..d7c29ef 100644
--- a/MicCorUEFCA2011_2011-06-27.crt
+++ b/0001-MicCorUEFCA2011_2011-06-27.crt
diff --git a/0002-snakeoil.crt b/0002-snakeoil.crt
new file mode 100644
index 0000000..dd02a82
--- /dev/null
+++ b/0002-snakeoil.crt
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIULTs+L+8XzClMGhAvyFIdsp/PYgUwDQYJKoZIhvcNAQEL
+BQAwSjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCENvbG9yYWRvMRUwEwYDVQQHDAxG
+b3J0IENvbGxpbnMxETAPBgNVBAoMCFNuYWtlT2lsMCAXDTIwMDkwNzE4NDMyMloY
+DzIxMjAwODE0MTg0MzIyWjBKMQswCQYDVQQGEwJVUzERMA8GA1UECAwIQ29sb3Jh
+ZG8xFTATBgNVBAcMDEZvcnQgQ29sbGluczERMA8GA1UECgwIU25ha2VPaWwwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIi65d6LmojD5S9q8vE/LI2HHQ
+boiO5/1KrFVc6kpxD6XdkJwpBoItYIfSls9CPnzvNWOAxR3hIeBd1U/prAPPxvQ1
+wuDLMXfWkcGaYHfPnme/YluAjnpuLH1MQcumgOzj5xYBvZZk+RbytX/phH7FW4Tx
++L1oBYnsfh3BSE/NTtEEHV1nXAXpa/dvyefWMlrlbwjfM5362lZzM6yrJGcOcWEy
+I66UYCIVO2Yhe/ZVF5B/tPGtd2oACz11xLeqLPM1WBjlekAG2Zi7UCPIvDCpdn5u
+Vna2ZRQmJyDDdh0Ja2VMC19dkMd/5nOAI21O+FvYPOkBWYX8f4DzDyVQlmIFAgMB
+AAGjUzBRMB0GA1UdDgQWBBRjuNXuXfh7mi8I3eTboeYGyFTa2zAfBgNVHSMEGDAW
+gBRjuNXuXfh7mi8I3eTboeYGyFTa2zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQBW2ckn0APqBnwSiOXCWkMCnvY7K7UOfxAlotEsMFSrkzdEa4IE
+sn0+A3RV/r3HZGqIaE8GMsBqp8UiVIbL5H67dkqvJEke94/7wEUC16JSSOBc0Mac
+HeArDWsL/WIbzKiVcRrmgX+XwJFlsUN5UtR/feTHR08yiy5srSCIJEqli/cTrOxS
+JAgvWPLxcoFhOKf6Mi+nwWdrQEbpXvvv8Jv/qyyz5e/VmTRY0wIVmUjd+Yseu+5M
+3+cpKtlYaawMxVni5RibA0A12fm+i60fGPrkCNhascUrNY+Oppaf/h+QmKOwEM7h
+pqKXyGFQyU6dB6cFBQ/uD5IABUYuEOuL7VFY
+-----END CERTIFICATE-----
diff --git a/0003-wrong.crt b/0003-wrong.crt
new file mode 100644
index 0000000..a9039db
--- /dev/null
+++ b/0003-wrong.crt
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEUzCCAzugAwIBAgIUa3jcFK7TK14FlPqQlHZGuPANPiMwDQYJKoZIhvcNAQEL
+BQAwgYoxCzAJBgNVBAYTAkdCMQ4wDAYDVQQIDAVDYW1iczESMBAGA1UEBwwJQ2Ft
+YnJpZGdlMQ8wDQYDVQQKDAZTbGVkZ2UxJTAjBgNVBAMMHFNlY3VyZSBCb290IFNp
+Z25pbmcgdGVzdCBrZXkxHzAdBgkqhkiG9w0BCQEWEHN0ZXZlQGVpbnZhbC5jb20w
+IBcNMTkwNDI4MjMyNDA1WhgPMjExOTA0MDQyMzI0MDVaMIGKMQswCQYDVQQGEwJH
+QjEOMAwGA1UECAwFQ2FtYnMxEjAQBgNVBAcMCUNhbWJyaWRnZTEPMA0GA1UECgwG
+U2xlZGdlMSUwIwYDVQQDDBxTZWN1cmUgQm9vdCBTaWduaW5nIHRlc3Qga2V5MR8w
+HQYJKoZIhvcNAQkBFhBzdGV2ZUBlaW52YWwuY29tMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArc9AXOIymkUfi3XJD1vZk7LR4cBVdQR71RGQxuM4omg/
+dvu+xaLH+hiwUGQ4/BvOBh95JcKg+ryCRWuILNTbYmxSB6TlJcv/JQgJ7iUmzVcH
+NFeEvtfV6Xd5zbh4wqK6ugtiqa7SGEhYKncHcbCL44gZdMKyamEeb5BMaL1VbBSQ
+baNeNUiaHeLwREBmNTi+tb0Btq1vTuzNzBN8baGk2RvP91gCUeCN+ILA0DXZ4a43
+flrLt+5d3go76p464htYB+Ab/Z/AFgEBWoKZm6MYZc8igCMdTyLY5Pn4FIX7r23T
+5g1u8pCf5MaEO1+ABxnVxim3hbvouABjlNj3BE2HZwIDAQABo4GsMIGpMB0GA1Ud
+DgQWBBSND9MzlvKlYN9gpyzKQaKbkq1kjTAfBgNVHSMEGDAWgBSND9MzlvKlYN9g
+pyzKQaKbkq1kjTAMBgNVHRMBAf8EAjAAMCsGA1UdJQQkMCIGCCsGAQUFBwMDBgor
+BgEEAYI3CgMGBgorBgEEAZIIEAECMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTANBgkqhkiG9w0BAQsFAAOCAQEABJDbjwU0Ua+B
+xFGfJlYFBlOzGa+Ppj0zQFonSnEA6zXVTOrnZulScKVUzEBaTA64TY6YdGELums+
+vGPiJ5/JWAY6FAiceXAEy+aIUXBHT1NQbis4kGOaYYiFCMkSOA1QH34/3zLVk3y1
+n//BSURlCh4amKoUZO3H1rS8hqQRYP4Hprv7TTJ+qvlKlKiYZcuODFyAusx+No1T
+5ht9dGzDtq9CX7Xi4BVHNVddEN1wKC8+9PlGB837DQUqofXGGt7C9fhIa10h/xWL
+KC+c5gaqgG8nBr7qVI7xlAqDq9Mr4ZoWwv43HGHno57d95blMj17aGjBRHF/Y2ak
+ebBQxN+Z7A==
+-----END CERTIFICATE-----
diff --git a/Makefile b/Makefile
index 4ac4ed7..bbee53c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,4 @@
-all: verify
+all: verify_combine
-verify:
- mkdir -p build
- # Verifying that the image is signed with the correct key.
- sbverify --cert MicCorUEFCA2011_2011-06-27.crt shim$(EFI_ARCH).efi.signed
- # Verifying that we have the correct binary.
- sbattach --detach build/detached-sig shim$(EFI_ARCH).efi.signed
- cp /usr/lib/shim/shim$(EFI_ARCH).efi build/shim$(EFI_ARCH).efi.signed
- sbattach --attach build/detached-sig build/shim$(EFI_ARCH).efi.signed
- cmp shim$(EFI_ARCH).efi.signed build/shim$(EFI_ARCH).efi.signed
- sha256sum shim$(EFI_ARCH).efi.signed build/shim$(EFI_ARCH).efi.signed
-
-clean:
- rm -rf build
+verify_combine:
+ ./verify_combine_sigs -a ${EFI_ARCH} shim*$(EFI_ARCH).efi*signed*
diff --git a/TEST-shimx64.efi.signed.multi b/TEST-shimx64.efi.signed.multi
new file mode 100644
index 0000000..e4430c3
--- /dev/null
+++ b/TEST-shimx64.efi.signed.multi
Binary files differ
diff --git a/TEST-shimx64.efi.signed.snakeoil b/TEST-shimx64.efi.signed.snakeoil
new file mode 100644
index 0000000..518f593
--- /dev/null
+++ b/TEST-shimx64.efi.signed.snakeoil
Binary files differ
diff --git a/debian/changelog b/debian/changelog
index a040b2a..ec43225 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+shim-signed (1.48) UNRELEASED; urgency=medium
+
+ * Add support for verifying and then combining signatures from
+ multiple signed shims.
+
+ WIP: needs a fixed version of sbverify to work...
+
+ -- Steve McIntyre <93sam@debian.org> Tue, 12 Apr 2026 15:11:11 +0100
+
shim-signed (1.47) unstable; urgency=medium
* update-secureboot-policy: do better checking around DKMS
diff --git a/debian/control b/debian/control
index 8357ba8..80eb196 100644
--- a/debian/control
+++ b/debian/control
@@ -11,7 +11,8 @@ Build-Depends: debhelper (>= 13),
# which broke our build
sbsigntool (>= 0.9.2-2),
po-debconf,
- debhelper-compat (= 13)
+ debhelper-compat (= 13),
+ python3-cryptography,
Standards-Version: 4.5.1
Vcs-Browser: https://salsa.debian.org/efi-team/shim-signed
Vcs-Git: https://salsa.debian.org/efi-team/shim-signed.git
diff --git a/shimaa64.efi.signed b/shimaa64.efi.signed.MS-2011
index bb54a7b..bb54a7b 100644
--- a/shimaa64.efi.signed
+++ b/shimaa64.efi.signed.MS-2011
Binary files differ
diff --git a/shimx64.efi.signed b/shimx64.efi.signed.MS-2011
index f2336f6..f2336f6 100644
--- a/shimx64.efi.signed
+++ b/shimx64.efi.signed.MS-2011
Binary files differ
diff --git a/verify_combine_sigs b/verify_combine_sigs
new file mode 100755
index 0000000..438f125
--- /dev/null
+++ b/verify_combine_sigs
@@ -0,0 +1,294 @@
+#!/usr/bin/python3
+#
+# verify_combine_sigs
+#
+# Helper script for shim-signed
+#
+# Microsoft currently only return signed binaries with one signature;
+# if they are signing with more than key/cert, then we will get
+# multiple separate signed binaries, one per key/cert.
+#
+# Check that all our signed shims are signed with an expected key that
+# we can remove and re-add; error out otherwise.
+#
+# Then finally we will add all those signatures to one output binary.
+#
+# Order of the listed signed shims matters here - list them *in the
+# same order* as the signatures we'd like in the final binary. It's
+# recommended to do this in the order:
+#
+# <oldest CA>
+# ...
+# <newest CA>
+#
+# as that is most likely to work with older firmware implementations.
+# That's most easily achieved by naming the signatures like
+# 0001-first-CA.crt, 0002-second-CA.crt, etc.
+
+import os
+import glob
+import sys
+import subprocess
+import argparse
+import shutil
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.serialization import pkcs7
+
+# Allowed certificates - each binary must be signed with a certificate
+# from this set.
+SIGN_CERTS = "0*.crt"
+
+# Path to sbverify - special build for now
+SBVERIFY = "/usr/bin/sbverify"
+
+
+def parse_args():
+ parser = argparse.ArgumentParser(description="verify_combine_sigs")
+ parser.add_argument(
+ "--efi_arch", "-a", help="EFI architecture for binaries", required=True
+ )
+ parser.add_argument("signed", help="signed binaries to verify/merge", nargs="+")
+ args = parser.parse_args()
+ return args
+
+
+def grab_cert_details(check_cert: str) -> (str, str):
+ """
+ Parse a certificate from disk and grab out:
+ - a hash of the certificate for comparison later
+ - the Subject test in a format matching the output of sbverify -l
+ """
+ print(f"Loading details from {check_cert}")
+ with open(check_cert, "rb") as inf:
+ pem_data = inf.read()
+ cert = x509.load_pem_x509_certificate(pem_data)
+ subject = "/" + "/".join([x.value for x in cert.subject])
+ sha256 = cert.fingerprint(hashes.SHA256()).hex()
+ print(f" - {subject}")
+ print(f" - sha256sum {sha256}")
+ return subject, sha256
+
+
+def list_signatures(signed_filename: str):
+ cmd = [SBVERIFY, "-l", signed_filename]
+ output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True)
+ for line in output.splitlines():
+ print(line)
+
+
+def verify_signature(signed_filename: str, certs: list[str]):
+ cmd = [SBVERIFY, signed_filename]
+ for cert in certs:
+ cmd.extend(["--cert", cert])
+ subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+
+
+def parse_sbverify(signed_filename: str) -> list[dict]:
+ cmd = [SBVERIFY, "-l", signed_filename]
+ output = subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT)
+ signatures = []
+ state = 0
+ for line in output.splitlines():
+ if line.startswith("signature"):
+ state = 1
+ continue
+ if state == 1 and line.startswith("image signature issuers:"):
+ state = 2
+ continue
+ if state == 2:
+ issuer = line[3:]
+ signatures.append(issuer)
+ state = 0
+ continue
+
+ return signatures
+
+
+def detach_signature(signed_filename: str, signum: int, outfile: str):
+ """
+ Detach a numbered signature from the signature table in a
+ signed binary.
+ """
+ cmd = [
+ "sbattach",
+ "--signum",
+ f"{signum}",
+ "--detach",
+ outfile,
+ signed_filename,
+ ]
+ subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT)
+
+
+def certs_in_detached_signature(detached: str) -> list[dict]:
+ """
+ Extract certificate details from a PKCS7 blob.
+ """
+ with open(detached, "rb") as inf:
+ pkcs7_data = inf.read()
+
+ output = []
+ certs = pkcs7.load_der_pkcs7_certificates(pkcs7_data)
+ for cert in certs:
+ subject = "/" + "/".join([x.value for x in cert.subject])
+ sha256 = cert.fingerprint(hashes.SHA256()).hex()
+ output.append({"sha256": sha256, "subject": subject})
+
+ # We want them in the order CA -> leaf
+ output.reverse()
+ return output
+
+
+def attach_sig(sigfile: str, unsigned: str):
+ """
+ Use sbattach to add a signature onto a binary.
+ """
+ cmd = [
+ "sbattach",
+ "--attach",
+ sigfile,
+ unsigned,
+ ]
+ subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT)
+
+
+def checksum_file(filename: str) -> str:
+ """
+ Calculate the sha256sum of a file
+ """
+ with open(filename, "rb") as inf:
+ data = inf.read()
+ hashalg = hashes.SHA256()
+ hasher = hashes.Hash(hashalg, backend=default_backend())
+ hasher.update(data)
+ digest = hasher.finalize()
+ return digest.hex()
+
+
+def main():
+
+ args = parse_args()
+
+ print("Loading details of all the expected certificates")
+ print("==========")
+ known_certs = {}
+ for check_cert in glob.glob(SIGN_CERTS):
+ subject, sha256 = grab_cert_details(check_cert)
+ known_certs[subject] = {"sha256": sha256, "filename": check_cert}
+ print("")
+
+ print(f"Verifying signatures for arch {args.efi_arch} ...")
+ print("==========\n")
+
+ build = "build"
+ shutil.rmtree(build, ignore_errors=True)
+ os.mkdir(build)
+
+ for signed in args.signed:
+
+ print(f"Checking {signed}")
+ print("----------\n")
+
+ # Verify that the image is signed and valid
+ print("Looking for any valid checksum and signature")
+ try:
+ verify_signature(signed, glob.glob(SIGN_CERTS))
+ except Exception as exc:
+ print(f"Invalid signature on {signed}: {exc}")
+ sys.exit(1)
+
+ signatures = parse_sbverify(signed)
+ num = len(signatures)
+ if num != 1:
+ print(f"Only expected 1 signature, but {signed} has {num}!")
+ print("Abort")
+ sys.exit(1)
+ # else
+ print(f"{signed} has 1 signature, good!")
+
+ # Now see what signature we have. We'll have to extract the
+ # signature table here, then extract the list of certificates
+ # included in the 1 signature we have.
+ detached_sig = "detached.sig"
+ detach_signature(signed, 1, detached_sig)
+ sig_certs = certs_in_detached_signature(detached_sig)
+
+ matched_filename = None
+
+ print("certs attached:")
+ for cert in sig_certs:
+ print(f' - {cert["subject"]}')
+ print(f' - {cert["sha256"]}')
+
+ # Now we need to compare the root certificate there to our
+ # known certificates
+ for subject, data in known_certs.items():
+ if (
+ sig_certs[0]["subject"] == subject
+ and sig_certs[0]["sha256"] == data["sha256"]
+ ):
+ print(
+ f'\nroot certificate matches a known certificate ({data["filename"]})'
+ )
+ matched_filename = data["filename"]
+
+ if matched_filename is None:
+ print(f"\nERROR: {signed} signature unknown, abort!")
+ sys.exit(1)
+
+ # Move the detached signature to one side, for future use
+ new_filename = os.path.join(build, f"detached-{matched_filename}")
+ shutil.move(detached_sig, new_filename)
+
+ # Copy our matching unsigned binary into the ${BUILD} directory.
+ unsigned = f"{build}/shim{args.efi_arch}.efi.signed"
+ shutil.copy(
+ f"/usr/lib/shim/shim{args.efi_arch}.efi",
+ unsigned,
+ )
+
+ # Attach the signature to our unsigned binary, so we know that
+ # the binary has not been tampered with during the signing
+ # process.
+ print("Checking the signature applies to our original binary")
+ attach_sig(new_filename, unsigned)
+ print(" Signature applies OK")
+
+ # Now compare the result to the signed binary we were given
+ print("Comparing the signed binaries")
+ old_sha = checksum_file(signed)
+ print(f"{old_sha} {signed}")
+ new_sha = checksum_file(unsigned)
+ print(f"{new_sha} {unsigned}")
+
+ if old_sha != new_sha:
+ print("\nERROR: signatures don't match, abort!")
+ sys.exit(1)
+
+ print("Binaries match!\n")
+
+ # If we've got this far, then we've checked all the binaries we
+ # were given and things look OK. Now we want to build a single
+ # output image with all the signatures attached.
+
+ print(f"Building final combined shim for arch {args.efi_arch} ...")
+ print("==========")
+
+ shutil.copy(
+ f"/usr/lib/shim/shim{args.efi_arch}.efi",
+ unsigned,
+ )
+
+ for sig in glob.glob(f"{build}/detached-*"):
+ print(f"Adding signature {sig}")
+ attach_sig(sig, unsigned)
+
+ # And finally show the list of signatures
+ print(f"Signatures on {unsigned} :")
+ list_signatures(unsigned)
+
+
+if __name__ == "__main__":
+ main()