From bc9ef7a42e812e532b556a045c72dc57ba1e19e1 Mon Sep 17 00:00:00 2001 From: Michal Domonkos Date: Thu, 24 Jul 2025 16:55:09 +0200 Subject: [PATCH] Add PQC readiness patches Resolves: RHEL-100571 RHEL-105421 --- rpm-4.19.x-multisig.patch | 2157 +++++++++++++++++++++++ rpm-4.19.x-pqc-algo.patch | 668 +++++++ rpm-4.19.x-rpmkeys-add-list-erase.patch | 466 +++++ rpm.spec | 17 +- 4 files changed, 3306 insertions(+), 2 deletions(-) create mode 100644 rpm-4.19.x-multisig.patch create mode 100644 rpm-4.19.x-pqc-algo.patch create mode 100644 rpm-4.19.x-rpmkeys-add-list-erase.patch diff --git a/rpm-4.19.x-multisig.patch b/rpm-4.19.x-multisig.patch new file mode 100644 index 0000000..99b27cf --- /dev/null +++ b/rpm-4.19.x-multisig.patch @@ -0,0 +1,2157 @@ +From 405836d5fe636391f552475b1b465e8da6407b38 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Wed, 9 Apr 2025 10:35:59 +0200 +Subject: [PATCH 01/15] Add macros.rpmsign-sequoia to doc dir + +Keep /usr/bin/sq hard coded. Automating the location would require +adding sequoia-sq as a build dependency which we don't want for now. +--- + docs/CMakeLists.txt | 1 + + docs/macros.rpmsign-sequoia | 26 ++++++++++++++++++++++++++ + 2 files changed, 27 insertions(+) + create mode 100644 docs/macros.rpmsign-sequoia + +diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt +index 4ba72839b..6d93ec8ca 100644 +--- a/docs/CMakeLists.txt ++++ b/docs/CMakeLists.txt +@@ -51,5 +51,6 @@ install(FILES + manual/tags.md + manual/triggers.md + manual/tsort.md ++ macros.rpmsign-sequoia + TYPE DOC + ) +diff --git a/docs/macros.rpmsign-sequoia b/docs/macros.rpmsign-sequoia +new file mode 100644 +index 000000000..b7f8966c7 +--- /dev/null ++++ b/docs/macros.rpmsign-sequoia +@@ -0,0 +1,26 @@ ++#============================================================================== ++# ---- Sequoia signature macros. ++# The signature to use and the location of configuration files for ++# signing packages with Sequoia. ++# ++# To enable signing with sequoia-sq, just copy this file to /etc/rpm: ++# cp /usr/share/doc/rpm/macros.rpmsign-sequoia /etc/rpm/ ++# ++# Unlike GnuPG, Sequoia doesn't support specifying the signer key by ++# email or name match, you need to supply the hex fingerprint (or keyid) ++#%_gpg_name ++#%_gpg_path ++ ++%__gpg /usr/bin/sq ++ ++# Macro(s) to hold the arguments passed to Sequoia for package ++# signing. Expansion result is parsed by popt, so be sure to use ++# %{shescape} where needed. ++# ++ ++%__gpg_sign_cmd %{__gpg} %{__gpg} sign \ ++ %{?_gpg_sign_cmd_extra_args} \ ++ %{?_gpg_name:--signer %{_gpg_name}} \ ++ --binary --signature-file %{shescape:%{?__signature_filename}} \ ++ %{?__plaintext_filename:-- %{shescape:%{__plaintext_filename}}} ++ +-- +2.50.1 + + +From 6190affb3bd410a3feb6456dda89cb09a8aeabe4 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 6 Nov 2024 09:44:34 +0200 +Subject: [PATCH 02/15] Drop unused header arguments, update comments in + signing internals + +The function comments are several generations old plain wrong. +No functional changes. + +(cherry picked from commit 51446f475393526c5731899a7e162ad2a713ee9e) +--- + sign/rpmgensig.c | 26 +++++++------------------- + 1 file changed, 7 insertions(+), 19 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index f0b4dc891..ca2ffe259 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -127,13 +127,8 @@ exit: + return rc; + } + +-/* +- * Validate generated signature and insert to header if it looks sane. +- * RPM doesn't support everything GPG does. Basic tests to see if the +- * generated signature is something we can use. +- * Return generated signature tag data on success, NULL on failure. +- */ +-static rpmtd makeSigTag(Header sigh, int ishdr, uint8_t *pkt, size_t pktlen) ++/* Wrap a raw signature in an rpmtd and sanity check, return NULL on fail */ ++static rpmtd makeSigTag(int ishdr, uint8_t *pkt, size_t pktlen) + { + pgpDigParams sigp = NULL; + rpmTagVal sigtag; +@@ -317,15 +312,8 @@ exit_nowait: + return rc; + } + +-/** +- * Generate GPG signature(s) for a header+payload file. +- * @param sigh signature header +- * @param ishdr header-only signature? +- * @param sigt signature target +- * @param passPhrase private key pass phrase +- * @return generated sigtag on success, 0 on failure +- */ +-static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt) ++/* Generate an OpenPGP signature(s) for a target */ ++static rpmtd makeGPGSignature(int ishdr, sigTarget sigt) + { + char * sigfile = rstrscat(NULL, sigt->fileName, ".sig", NULL); + struct stat st; +@@ -363,7 +351,7 @@ static rpmtd makeGPGSignature(Header sigh, int ishdr, sigTarget sigt) + rpmlog(RPMLOG_DEBUG, "Got %zd bytes of OpenPGP sig\n", pktlen); + + /* Parse the signature, change signature tag as appropriate. */ +- sigtd = makeSigTag(sigh, ishdr, pkt, pktlen); ++ sigtd = makeSigTag(ishdr, pkt, pktlen); + exit: + (void) unlink(sigfile); + free(sigfile); +@@ -418,7 +406,7 @@ static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) + rpmtd sigtd = NULL; + + /* Make the cheaper v4 signature first */ +- if ((sigtd = makeGPGSignature(sigh, 1, sigt_v4)) == NULL) ++ if ((sigtd = makeGPGSignature(1, sigt_v4)) == NULL) + goto exit; + + /* See if we already have a signature by the same key and parameters */ +@@ -436,7 +424,7 @@ static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) + rpmtdFree(sigtd); + + /* Assume the same signature test holds for v3 signature too */ +- if ((sigtd = makeGPGSignature(sigh, 0, sigt_v3)) == NULL) ++ if ((sigtd = makeGPGSignature(0, sigt_v3)) == NULL) + goto exit; + + if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) +-- +2.50.1 + + +From 373482f77336c7b67b7c9b0822ad083090f9addb Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 6 Nov 2024 10:18:34 +0200 +Subject: [PATCH 03/15] Refactor signature insertion to a helper function + +No functional changes, just a tiny refactoring bit for the next steps. + +(cherry picked from commit b760b9ca1285cddaa29059eee2099e8b3af1f5f0) +--- + sign/rpmgensig.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index ca2ffe259..70426555b 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -400,6 +400,11 @@ static int haveSignature(rpmtd sigtd, Header h) + return rc; + } + ++static int putSignature(Header sigh, rpmtd sigtd) ++{ ++ return (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0); ++} ++ + static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) + { + int rc = -1; +@@ -417,7 +422,7 @@ static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) + /* Nuke all signature tags */ + deleteSigs(sigh); + +- if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) ++ if (putSignature(sigh, sigtd)) + goto exit; + + if (sigt_v3) { +@@ -427,7 +432,7 @@ static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) + if ((sigtd = makeGPGSignature(0, sigt_v3)) == NULL) + goto exit; + +- if (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) ++ if (putSignature(sigh, sigtd)) + goto exit; + } + +-- +2.50.1 + + +From 895e54852a53a610a1e703ce902f3ff3c5360e32 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 6 Nov 2024 10:30:15 +0200 +Subject: [PATCH 04/15] Simplify the v3 signature logic a bit + +Pass the flags down to the function that needs to do stuff. No +functional changes. + +(cherry picked from commit 41552eee5ba97e3aad9441306fa0cbdd2a28574a) +--- + sign/rpmgensig.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 70426555b..55826446d 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -405,7 +405,8 @@ static int putSignature(Header sigh, rpmtd sigtd) + return (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0); + } + +-static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) ++static int replaceSignature(Header sigh, rpmSignFlags flags, ++ sigTarget sigt_v3, sigTarget sigt_v4) + { + int rc = -1; + rpmtd sigtd = NULL; +@@ -425,7 +426,7 @@ static int replaceSignature(Header sigh, sigTarget sigt_v3, sigTarget sigt_v4) + if (putSignature(sigh, sigtd)) + goto exit; + +- if (sigt_v3) { ++ if (flags & RPMSIGN_FLAG_RPMV3) { + rpmtdFree(sigtd); + + /* Assume the same signature test holds for v3 signature too */ +@@ -642,7 +643,6 @@ static int rpmSign(const char *rpm, int deleting, int flags) + deleteSigs(sigh); + } else { + /* Signature target containing header + payload */ +- int v3 = (flags & RPMSIGN_FLAG_RPMV3); + sigt_v3.fd = fd; + sigt_v3.start = headerStart; + sigt_v3.fileName = rpm; +@@ -652,7 +652,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + sigt_v4 = sigt_v3; + sigt_v4.size = headerSizeof(h, HEADER_MAGIC_YES); + +- res = replaceSignature(sigh, v3 ? &sigt_v3 : NULL, &sigt_v4); ++ res = replaceSignature(sigh, flags, &sigt_v3, &sigt_v4); + if (res != 0) { + if (res == 1) { + rpmlog(RPMLOG_WARNING, +-- +2.50.1 + + +From 5d5f760c02ddb068a1dcb1d916557b053dffd3c4 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Fri, 4 Jul 2025 11:10:19 +0200 +Subject: [PATCH 05/15] Add rpmdump utility + +--- + tools/CMakeLists.txt | 3 +- + tools/rpmdump.c | 236 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 238 insertions(+), 1 deletion(-) + create mode 100644 tools/rpmdump.c + +diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt +index f4fbefecc..5be4cf2d0 100644 +--- a/tools/CMakeLists.txt ++++ b/tools/CMakeLists.txt +@@ -12,6 +12,7 @@ add_executable(rpmdeps rpmdeps.c) + add_executable(rpmgraph rpmgraph.c) + add_executable(rpmlua rpmlua.c) + add_executable(rpmuncompress rpmuncompress.c) ++add_executable(rpmdump rpmdump.c) + + target_link_libraries(rpmsign PRIVATE librpmsign) + target_link_libraries(rpmlua PRIVATE LUA::LUA) +@@ -62,5 +63,5 @@ install(TARGETS + rpm rpmdb rpmkeys rpm2cpio rpmsign rpmbuild rpmspec + rpmlua rpmgraph + ) +-install(TARGETS rpmdeps rpmuncompress DESTINATION ${RPM_CONFIGDIR}) ++install(TARGETS rpmdeps rpmdump rpmuncompress DESTINATION ${RPM_CONFIGDIR}) + +diff --git a/tools/rpmdump.c b/tools/rpmdump.c +new file mode 100644 +index 000000000..329ce5978 +--- /dev/null ++++ b/tools/rpmdump.c +@@ -0,0 +1,236 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct entryInfo { ++ uint32_t tag; ++ uint32_t type; ++ int32_t offset; ++ uint32_t count; ++}; ++ ++static const char * const tagTypeNames[] = { ++ "(null)", "char", "int8", "int16", "int32", "int64", ++ "string", "blob", "argv", "i18nstring" ++}; ++ ++static const char *sigTagName(uint32_t tag) ++{ ++ const char *n = "(unknown)"; ++ switch (tag) { ++ case RPMTAG_HEADERSIGNATURES: return "Headersignatures"; ++ case RPMTAG_HEADERIMAGE: return "Headerimage"; ++ case RPMSIGTAG_SIZE: return "Size"; ++ case RPMSIGTAG_LEMD5_1: return "Lemd5_1"; ++ case RPMSIGTAG_PGP: return "Pgp"; ++ case RPMSIGTAG_LEMD5_2: return "Lemd5_2"; ++ case RPMSIGTAG_MD5: return "Md5"; ++ case RPMSIGTAG_GPG: return "Gpg"; ++ case RPMSIGTAG_PGP5: return "Pgp5"; ++ case RPMSIGTAG_PAYLOADSIZE: return "Payloadsize"; ++ case RPMSIGTAG_RESERVEDSPACE: return "Reservedspace"; ++ case RPMSIGTAG_BADSHA1_1: return "Badsha1_1"; ++ case RPMSIGTAG_BADSHA1_2: return "Badsha1_2"; ++ case RPMSIGTAG_SHA1: return "Sha1"; ++ case RPMSIGTAG_DSA: return "Dsa"; ++ case RPMSIGTAG_RSA: return "Rsa"; ++ case RPMSIGTAG_LONGSIZE: return "Longsize"; ++ case RPMSIGTAG_LONGARCHIVESIZE: return "Longarchivesize"; ++ case RPMSIGTAG_SHA256: return "Sha256"; ++ case RPMSIGTAG_FILESIGNATURES: return "Filesignatures"; ++ case RPMSIGTAG_FILESIGNATURELENGTH: return "filesignaturelength"; ++ case RPMSIGTAG_VERITYSIGNATURES: return "veritysignatures"; ++ case RPMSIGTAG_VERITYSIGNATUREALGO: return "veritysignaturealgo"; ++ case RPMSIGTAG_RESERVED: return "Reserved"; ++ default: ++ break; ++ } ++ return n; ++} ++ ++struct rpmlead_s { ++ unsigned char magic[4]; ++ unsigned char major; ++ unsigned char minor; ++ short type; ++ short archnum; ++ char name[66]; ++ short osnum; ++ short signature_type; ++ char reserved[16]; ++}; ++ ++static int readlead(int fd) ++{ ++ ++ struct rpmlead_s lead; ++ if (read(fd, &lead, 96) != 96) { ++ fprintf(stderr, "failed to read lead\n"); ++ return 1; ++ } ++ ++ printf("Lead magic: %hhx%hhx%hhx%hhx\n", ++ lead.magic[0], lead.magic[1], lead.magic[2], lead.magic[3]); ++ printf("Version %d.%d\n", lead.major, lead.minor); ++ printf("Type: %hu\n", ntohs(lead.type)); ++ printf("Name: %s\n", lead.name); ++ printf("Arch: %hu\n", ntohs(lead.archnum)); ++ printf("OS: %hu\n", ntohs(lead.osnum)); ++ printf("Sigtype: %hu\n", ntohs(lead.signature_type)); ++ printf("\n"); ++ ++ return 0; ++} ++ ++static void dumptag(struct entryInfo *entry, int sighdr, const char *pfx) ++{ ++ const char *tagn; ++ uint32_t tag = htonl(entry->tag); ++ if (sighdr) ++ tagn = sigTagName(tag); ++ else ++ tagn = rpmTagGetName(tag); ++ ++ printf("%stagno: %4d (%s)\n", pfx, tag, tagn); ++ printf("%stype: %4d (%s)\n", pfx, htonl(entry->type), ++ tagTypeNames[htonl(entry->type)]); ++ printf("%soffset: %4d\n", pfx, htonl(entry->offset)); ++ printf("%scount: %4d\n", pfx, htonl(entry->count)); ++} ++ ++static int readhdr(int fd, int sighdr, const char *msg) ++{ ++ uint32_t intro[4], outro[4]; ++ uint32_t numEntries, numBytes, headerLen, indexLen; ++ uint32_t *blob = NULL; ++ uint8_t *dataStart; ++ struct entryInfo *pe; ++ struct entryInfo * entry = NULL; ++ int rc = 1, i; ++ ++ if (read(fd, intro, sizeof(intro)) != sizeof(intro)) { ++ fprintf(stderr, "header intro fail"); ++ goto exit; ++ } ++ ++ printf("%s:\n", msg); ++ ++ printf("Header magic: %x (reserved: %x)\n", intro[0], intro[1]); ++ ++ numEntries = ntohl(intro[2]); ++ numBytes = ntohl(intro[3]); ++ indexLen = numEntries * sizeof(*entry); ++ headerLen = indexLen + numBytes; ++ ++ blob = (uint32_t *)malloc(sizeof(numEntries) + sizeof(numBytes) + headerLen); ++ blob[0] = htonl(numEntries); ++ blob[1] = htonl(numBytes); ++ ++ pe = (struct entryInfo *) &(blob[2]); ++ dataStart = (uint8_t *) (pe + numEntries); ++ ++ printf("Index entries: %d (%u bytes)\n", numEntries, indexLen); ++ printf("Data size: %d bytes\n", numBytes); ++ printf("Header size: %d bytes\n", headerLen); ++ ++ ++ if (read(fd, blob+2, headerLen) != headerLen) { ++ fprintf(stderr, "reading %d bytes of header fail\n", headerLen); ++ goto exit; ++ } ++ ++ /* signature header padding */ ++ if (sighdr) { ++ int pad = (8 - (headerLen % 8)) % 8; ++ printf("Padding: %d bytes", pad); ++ if (read(fd, outro, pad) != pad) { ++ fprintf(stderr, "failed reading padding %d\n", pad); ++ goto exit; ++ } ++ } ++ ++{ ++ entry = (struct entryInfo *) (blob + 2); ++ uint32_t tag = htonl(entry->tag); ++ struct entryInfo _trailer, *trailer = &_trailer; ++ int32_t toffset = 0; ++ uint8_t *regionEnd = NULL; ++ uint32_t ril = 0, rdl = 0; ++ ++ memset(trailer, 0, sizeof(*trailer)); ++ if (tag == 62 || tag == 63) { ++ /* The trailer isn't guaranteed to be aligned, copy required */ ++ memcpy(trailer, dataStart + htonl(entry->offset), sizeof(*trailer)); ++ toffset = -htonl(trailer->offset); ++ regionEnd = dataStart + toffset + 16; ++ rdl = regionEnd - dataStart; ++ ril = toffset/sizeof(*pe); ++ printf("\nRegion entries %d\n", ril); ++ printf("Region size %d\n", rdl); ++ printf("Dribbles: %d\n", numEntries-ril); ++ } ++ ++ for (i = 0; i < numEntries; i++, entry++) { ++ int in_region = (ril > 0 && i < ril); ++ const char *marker = ""; ++ if (in_region) { ++ marker = (i < ril) ? "[region]" : "[dribble]"; ++ } ++ tag = htonl(entry->tag); ++ printf("\nTag #%d %s\n", i, marker); ++ dumptag(entry, sighdr, "\t"); ++ ++ if (tag == 62 || tag == 63) { ++ printf("\n\tregion trailer\n"); ++ dumptag(trailer, sighdr, "\t\t"); ++ } ++ } ++} ++ ++ printf("\n"); ++ rc = 0; ++ ++exit: ++ free(blob); ++ return rc; ++} ++ ++static int readpkg(int fd) ++{ ++ int rc = 1; ++ ++ if (readlead(fd)) ++ return rc; ++ ++ /* signature header */ ++ if (readhdr(fd, 1, "Signature")) ++ return rc; ++ ++ /* main header */ ++ if (readhdr(fd, 0, "Header")) ++ return rc; ++ ++ /* payload ... */ ++ ++ return 0; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int i; ++ for (i = 1; i < argc; i++) { ++ int fd = open(argv[i], O_RDONLY); ++ if (fd < 0) ++ continue; ++ if (readpkg(fd)) ++ fprintf(stderr, "bad package %s\n", argv[i]); ++ close(fd); ++ } ++} +-- +2.50.1 + + +From 4eb67554bdd79eca204740c70798e06adfc75066 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 20 Feb 2024 11:54:17 +0200 +Subject: [PATCH 06/15] Add a new unique v6-only reserved signature tag below + header base + +Unfortunately RPMSIGTAG_RESERVEDSPACE clashes with RPMTAG_INSTALLTIME +so we can't use it in v6. Introduce a new tag at the top of the +signature range to make sure its always the last one - on v6 packages +that is. For v4 packages we're better off not messing with the existing +tag at this point, we just need to handle both. + +(backported from commit a40b6e9a74b517370fda9aa5c9fc3db2276be563) +--- + include/rpm/rpmtag.h | 3 +++ + sign/rpmgensig.c | 8 ++++++-- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/include/rpm/rpmtag.h b/include/rpm/rpmtag.h +index 8da7cf518..4009f776d 100644 +--- a/include/rpm/rpmtag.h ++++ b/include/rpm/rpmtag.h +@@ -23,6 +23,7 @@ extern "C" { + #define HEADER_REGIONS 64 + #define HEADER_I18NTABLE 100 + #define HEADER_SIGBASE 256 ++#define HEADER_SIGTOP 999 + #define HEADER_TAGBASE 1000 + + /** \ingroup rpmtag +@@ -67,6 +68,7 @@ typedef enum rpmTag_e { + /* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURELENGTH */ + RPMTAG_VERITYSIGNATURES = RPMTAG_SIG_BASE+20, /* s[] */ + RPMTAG_VERITYSIGNATUREALGO = RPMTAG_SIG_BASE+21, /* i */ ++ RPMTAG_SIG_TOP = HEADER_SIGTOP, + + RPMTAG_NAME = 1000, /* s */ + #define RPMTAG_N RPMTAG_NAME /* s */ +@@ -447,6 +449,7 @@ typedef enum rpmSigTag_e { + RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 19, + RPMSIGTAG_VERITYSIGNATURES = RPMTAG_VERITYSIGNATURES, + RPMSIGTAG_VERITYSIGNATUREALGO = RPMTAG_VERITYSIGNATUREALGO, ++ RPMSIGTAG_RESERVED = RPMTAG_SIG_TOP, + } rpmSigTag; + + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 55826446d..cd11d0919 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -582,6 +582,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + struct sigTarget_s sigt_v4; + unsigned int origSigSize; + int insSig = 0; ++ rpmTagVal reserveTag = RPMSIGTAG_RESERVEDSPACE; + + fprintf(stdout, "%s:\n", rpm); + +@@ -666,11 +667,14 @@ static int rpmSign(const char *rpm, int deleting, int flags) + res = -1; + } + ++ /* Only v6 packages have this */ ++ if (headerIsEntry(h, RPMSIGTAG_RESERVED)) ++ reserveTag = RPMSIGTAG_RESERVED; ++ + /* Adjust reserved size for added/removed signatures */ +- if (headerGet(sigh, RPMSIGTAG_RESERVEDSPACE, &utd, HEADERGET_MINMEM)) { ++ if (headerGet(sigh, reserveTag, &utd, HEADERGET_MINMEM)) { + unsigned newSize = headerSizeof(sigh, HEADER_MAGIC_YES); + int diff = newSize - origSigSize; +- + /* diff can be zero if nothing was added or removed */ + if (diff) { + utd.count -= diff; +-- +2.50.1 + + +From 2c28b0134b1aaddea7082432401312e2a7588494 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 20 Nov 2024 15:49:35 +0200 +Subject: [PATCH 07/15] Add tag extension for rpm format version detection + +v6 packages have RPMTAG_RPMFORMAT, but we need to differentiate between +3 and 4 too in various places. As such this is a rather simple-minded +heuristic but it at least gives us a central place where to manage that +detection. + +Contains part of af81cb8cc1896000928fc87280624166c347edd2 + +(backported from commit ea46f4fd83a7ed39594982edbe9e3cbabe3a7abe) +--- + docs/manual/tags.md | 1 + + include/rpm/rpmtag.h | 1 + + lib/tagexts.c | 17 +++++++++++++++++ + 3 files changed, 19 insertions(+) + +diff --git a/docs/manual/tags.md b/docs/manual/tags.md +index fef69ab64..8e7ef8f74 100644 +--- a/docs/manual/tags.md ++++ b/docs/manual/tags.md +@@ -453,6 +453,7 @@ Obsoletenevrs | 5043 | string array | Formatted `name [op version]` obso + Enhancenevrs | 5061 | string array | Formatted `name [op version]` enhance dependency strings. + Recommendnevrs | 5058 | string array | Formatted `name [op version]` recommend dependency strings. + Requirenevrs | 5041 | string array | Formatted `name [op version]` require dependency strings. ++Rpmformat | 5114 | int32 | Detected rpm format version (3/4/6) + Suggestnevrs | 5059 | string array | Formatted `name [op version]` suggest dependency strings. + Supplementnevrs | 5060 | string array | Formatted `name [op version]` supplement dependency strings. + Sysusers | 5109 | string array | Formatted systemd-sysusers lines for the package. | +diff --git a/include/rpm/rpmtag.h b/include/rpm/rpmtag.h +index 4009f776d..2ac79ce42 100644 +--- a/include/rpm/rpmtag.h ++++ b/include/rpm/rpmtag.h +@@ -388,6 +388,7 @@ typedef enum rpmTag_e { + RPMTAG_PREUNTRANSFLAGS = 5107, /* i */ + RPMTAG_POSTUNTRANSFLAGS = 5108, /* i */ + RPMTAG_SYSUSERS = 5109, /* s[] extension */ ++ RPMTAG_RPMFORMAT = 5114, /* i */ + RPMTAG_PACKAGEDIGESTS = 5118, /* s[] */ + RPMTAG_PACKAGEDIGESTALGOS = 5119, /* i[] */ + +diff --git a/lib/tagexts.c b/lib/tagexts.c +index a07ceb5c8..713fa95d5 100644 +--- a/lib/tagexts.c ++++ b/lib/tagexts.c +@@ -1011,6 +1011,22 @@ static int sysusersTag(Header h, rpmtd td, headerGetFlags hgflags) + return (td->count > 0); + } + ++static int rpmformatTag(Header h, rpmtd td, headerGetFlags hgflags) ++{ ++ if (headerGet(h, RPMTAG_RPMFORMAT, td, HEADERGET_ALLOC)) ++ return 1; ++ ++ uint32_t *nump = (uint32_t *)xcalloc(1, sizeof(*nump)); ++ *nump = headerIsEntry(h, RPMTAG_HEADERIMMUTABLE) ? 4 : 3; ++ ++ td->data = nump; ++ td->count = 1; ++ td->type = RPM_INT32_TYPE; ++ td->flags = RPMTD_ALLOCED; ++ ++ return 1; ++} ++ + static const struct headerTagFunc_s rpmHeaderTagExtensions[] = { + { RPMTAG_GROUP, groupTag }, + { RPMTAG_DESCRIPTION, descriptionTag }, +@@ -1051,6 +1067,7 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = { + { RPMTAG_CONFLICTNEVRS, conflictnevrsTag }, + { RPMTAG_FILENLINKS, filenlinksTag }, + { RPMTAG_SYSUSERS, sysusersTag }, ++ { RPMTAG_RPMFORMAT, rpmformatTag }, + { 0, NULL } + }; + +-- +2.50.1 + + +From c602f37a6adec620d2ccf8363809d427f2c1cbd5 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 30 Oct 2024 16:46:26 +0200 +Subject: [PATCH 08/15] Add support for multiple OpenPGP signatures per + package, part 1/2 + +Add support for multiple OpenPGP header signatures per package, base64 +encoded in a string array, also known as rpm v6 signatures. + +--addsign no longer deletes any signatures, it only creates and adds a +new signature if possible. --delete and --resign behave as before: they +delete ALL signatures on the package, and the latter then creates and +adds a new one. + +For v6 packages this is the default signature type, but if requested, +one v4 compat signature can be created for compatible algorithms. v6 +signatures on v4 packages are also supported, but have to be explicitly +requested. In that case, v3/v4 signatures are only added if none already +exist and a v4 compatible algorithm is used. v3 signatures on v6 +packages are not supported (out of principle, not a technical +limitation) + +On verification, if RPMTAG_OPENPGP exists then other signature tags are +ignored because they're expected to only contain compat copies of the +same content. As of now, all existing signatures must validate for +signature checking of a package to pass, further policies are to be +added later. + +Fixes: #3385 + +(backported from commit 5630cf4bcbaae85d1b53e7a57ba6c7ed86b62e7d) +--- + docs/man/rpmsign.8.md | 48 +++++++++++++--- + docs/manual/tags.md | 2 + + include/rpm/rpmsign.h | 3 + + include/rpm/rpmtag.h | 2 + + include/rpm/rpmts.h | 3 + + lib/package.c | 1 + + lib/rpmvs.c | 46 ++++++++++++--- + sign/rpmgensig.c | 130 +++++++++++++++++++++++++++++++++--------- + tools/rpmsign.c | 12 +++- + 9 files changed, 200 insertions(+), 47 deletions(-) + +diff --git a/docs/man/rpmsign.8.md b/docs/man/rpmsign.8.md +index 339e28451..d6deebec2 100644 +--- a/docs/man/rpmsign.8.md ++++ b/docs/man/rpmsign.8.md +@@ -25,22 +25,25 @@ SIGNING PACKAGES: + rpmsign-options + --------------- + +-\[**\--rpmv3**\] \[**\--fskpath ***KEY*\] \[**\--signfiles**\] ++\[**\--rpmv3**\] \[**\--rpmv4**\] \[**\--rpmv6**\] \[**\--fskpath ***KEY*\] \[**\--signfiles**\] + + DESCRIPTION + =========== + +-Both of the **\--addsign** and **\--resign** options generate and insert +-new signatures for each package *PACKAGE\_FILE* given, replacing any +-existing signatures. There are two options for historical reasons, there +-is no difference in behavior currently. ++**rpmsign** **\--addsign** generates and inserts a new OpenPGP signature ++for each *PACKAGE\_FILE* given unless a signature with identical ++parameters already exists, in which case no action is taken. ++Arbitrary number of V6 signatures can be added. ++ ++**rpmsign** **\--resign** generates and inserts a new OpenPGP signature ++for each *PACKAGE\_FILE*, replacing any and all previous signatures. + + To create a signature rpmsign needs to verify the package\'s checksum. As a +-result packages with a MD5/SHA1 checksums cannot be signed in FIPS mode. ++result V4 packages with MD5/SHA1 checksums cannot be signed in FIPS mode. + + **rpmsign** **\--delsign** *PACKAGE\_FILE \...* + +-Delete all signatures from each package *PACKAGE\_FILE* given. ++Delete all OpenPGP signatures from each package *PACKAGE\_FILE* given. + + **rpmsign** **\--delfilesign** *PACKAGE\_FILE \...* + +@@ -52,13 +55,40 @@ SIGN OPTIONS + + **\--rpmv3** + +-: Force RPM V3 header+payload signature addition. These are expensive ++: Request RPM V3 header+payload signature addition on V4 packages. ++ These signatures are expensive + and redundant baggage on packages where a separate payload digest + exists (packages built with rpm \>= 4.14). Rpmsign will automatically + detect the need for V3 signatures, but this option can be used to +- force their creation if the packages must be fully signature ++ request their creation if the packages must be fully signature + verifiable with rpm \< 4.14 or other interoperability reasons. + ++ Has no effect when signing V6 packages. ++ ++**\--rpmv4** ++ ++: Request RPM V4 header signature addition on V6 packages. ++ Useful for making V6 packages signature verifiable ++ with rpm 4.x versions. ++ ++ V4 compatibility signatures are only ever added if the signing algorithm ++ is one of those known to V4: RSA, EcDSA, EdDSA (and original DSA). ++ Only one V4 signature can be present in a package, so this is ++ added only on the first **\--addsign** with a V4 compatible ++ algorithm, and ignored otherwise. ++ ++ Has no effect when signing V4 packages. ++ ++**\--rpmv6** ++ ++: Request RPM V6 header signature addition on V4 packages. ++ ++ This generally always succeeds as there can be arbitrary number of ++ V6 signatures on a package. A V3/V4 compatibility signatures are ++ added usign the same logic as **\--rpmv4** on a V6 package. ++ ++ Has no effect when signing V6 packages. ++ + **\--fskpath ***KEY* + + : Used with **\--signfiles**, use file signing key *Key*. +diff --git a/docs/manual/tags.md b/docs/manual/tags.md +index 8e7ef8f74..2b24083c3 100644 +--- a/docs/manual/tags.md ++++ b/docs/manual/tags.md +@@ -304,6 +304,7 @@ Tag Name | Value| Type | Description + ------------------|------|--------------|------------ + Dsaheader | 267 | bin | OpenPGP DSA signature of the header (if thus signed) + Longsigsize | 270 | int64 | Header+payload size if > 4GB. ++Openpgp | 278 | string array | OpenPGP signature(s) of the header, base64 encoded + Payloaddigest | 5092 | string array | Cryptographic digest of the compressed payload. + Payloaddigestalgo | 5093 | int32 | ID of the payload digest algorithm. + Payloaddigestalt | 5097 | string array | Cryptographic digest of the uncompressed payload. +@@ -446,6 +447,7 @@ Longsigsize | Header+payload size in 64bit format + + Tag Name | Value| Type | Description + ----------------------|------|--------------|------------ ++Openpgp | 278 | string array | All OpenPGP signature(s) in the header, including legacy ones (base64 encoded) + Origfilenames | 5007 | string array | Original Filenames in relocated packages. + Providenevrs | 5042 | string array | Formatted `name [op version]` provide dependency strings. + Conflictnevrs | 5044 | string array | Formatted `name [op version]` conflict dependency strings. +diff --git a/include/rpm/rpmsign.h b/include/rpm/rpmsign.h +index c876ef1bd..c0dbdab53 100644 +--- a/include/rpm/rpmsign.h ++++ b/include/rpm/rpmsign.h +@@ -18,6 +18,9 @@ enum rpmSignFlags_e { + RPMSIGN_FLAG_IMA = (1 << 0), + RPMSIGN_FLAG_RPMV3 = (1 << 1), + RPMSIGN_FLAG_FSVERITY = (1 << 2), ++ RPMSIGN_FLAG_RESIGN = (1 << 3), ++ RPMSIGN_FLAG_RPMV4 = (1 << 4), ++ RPMSIGN_FLAG_RPMV6 = (1 << 5), + }; + typedef rpmFlags rpmSignFlags; + +diff --git a/include/rpm/rpmtag.h b/include/rpm/rpmtag.h +index 2ac79ce42..43743d48c 100644 +--- a/include/rpm/rpmtag.h ++++ b/include/rpm/rpmtag.h +@@ -68,6 +68,7 @@ typedef enum rpmTag_e { + /* RPMTAG_SIG_BASE+19 reserved for RPMSIGTAG_FILESIGNATURELENGTH */ + RPMTAG_VERITYSIGNATURES = RPMTAG_SIG_BASE+20, /* s[] */ + RPMTAG_VERITYSIGNATUREALGO = RPMTAG_SIG_BASE+21, /* i */ ++ RPMTAG_OPENPGP = RPMTAG_SIG_BASE+22, /* s[] */ + RPMTAG_SIG_TOP = HEADER_SIGTOP, + + RPMTAG_NAME = 1000, /* s */ +@@ -450,6 +451,7 @@ typedef enum rpmSigTag_e { + RPMSIGTAG_FILESIGNATURELENGTH = RPMTAG_SIG_BASE + 19, + RPMSIGTAG_VERITYSIGNATURES = RPMTAG_VERITYSIGNATURES, + RPMSIGTAG_VERITYSIGNATUREALGO = RPMTAG_VERITYSIGNATUREALGO, ++ RPMSIGTAG_OPENPGP = RPMTAG_OPENPGP, + RPMSIGTAG_RESERVED = RPMTAG_SIG_TOP, + } rpmSigTag; + +diff --git a/include/rpm/rpmts.h b/include/rpm/rpmts.h +index 7b60338d7..5c168820f 100644 +--- a/include/rpm/rpmts.h ++++ b/include/rpm/rpmts.h +@@ -105,6 +105,7 @@ enum rpmVSFlags_e { + RPMVSF_NOSHA256HEADER = (1 << 9), + RPMVSF_NODSAHEADER = (1 << 10), + RPMVSF_NORSAHEADER = (1 << 11), ++ RPMVSF_NOOPENPGP = (1 << 12), + /* bit(s) 12-15 unused */ + RPMVSF_NOPAYLOAD = (1 << 16), + RPMVSF_NOMD5 = (1 << 17), +@@ -125,6 +126,7 @@ typedef rpmFlags rpmVSFlags; + #define RPMVSF_MASK_NOSIGNATURES \ + ( RPMVSF_NODSAHEADER | \ + RPMVSF_NORSAHEADER | \ ++ RPMVSF_NOOPENPGP | \ + RPMVSF_NODSA | \ + RPMVSF_NORSA ) + #define _RPMVSF_NOSIGNATURES RPMVSF_MASK_NOSIGNATURES +@@ -132,6 +134,7 @@ typedef rpmFlags rpmVSFlags; + #define RPMVSF_MASK_NOHEADER \ + ( RPMVSF_NOSHA1HEADER | \ + RPMVSF_NOSHA256HEADER | \ ++ RPMVSF_NOOPENPGP | \ + RPMVSF_NODSAHEADER | \ + RPMVSF_NORSAHEADER ) + #define _RPMVSF_NOHEADER RPMVSF_MASK_NOHEADER +diff --git a/lib/package.c b/lib/package.c +index 8eee3684a..000f2fcdd 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -53,6 +53,7 @@ static struct taglate_s { + { RPMSIGTAG_RSA, RPMTAG_RSAHEADER, 0, 0 }, + { RPMSIGTAG_LONGSIZE, RPMTAG_LONGSIGSIZE, 1, 0 }, + { RPMSIGTAG_LONGARCHIVESIZE, RPMTAG_LONGARCHIVESIZE, 1, 0 }, ++ { RPMSIGTAG_OPENPGP, RPMTAG_OPENPGP, 0, 0 }, + { 0 } + }; + +diff --git a/lib/rpmvs.c b/lib/rpmvs.c +index 96c506e56..e22051a92 100644 +--- a/lib/rpmvs.c ++++ b/lib/rpmvs.c +@@ -1,6 +1,7 @@ + #include "system.h" + + #include ++#include + #include + #include + #include +@@ -27,6 +28,7 @@ struct vfytag_s { + }; + + static const struct vfytag_s rpmvfytags[] = { ++ { RPMTAG_OPENPGP, RPM_STRING_ARRAY_TYPE, 0, 0, }, + { RPMSIGTAG_SIZE, RPM_BIN_TYPE, 0, 0, }, + { RPMSIGTAG_PGP, RPM_BIN_TYPE, 0, 0, }, + { RPMSIGTAG_MD5, RPM_BIN_TYPE, 0, 16, }, +@@ -51,6 +53,9 @@ struct vfyinfo_s { + }; + + static const struct vfyinfo_s rpmvfyitems[] = { ++ { RPMTAG_OPENPGP, 1, ++ { RPMSIG_SIGNATURE_TYPE, RPMVSF_NOOPENPGP, ++ (RPMSIG_HEADER), 0, 0, }, }, + { RPMSIGTAG_SIZE, 1, + { RPMSIG_OTHER_TYPE, 0, + (RPMSIG_HEADER|RPMSIG_PAYLOAD), 0, 0, }, }, +@@ -132,7 +137,7 @@ exit: + return valid; + } + +-static void rpmsinfoInit(const struct vfyinfo_s *vinfo, ++static rpmRC rpmsinfoInit(const struct vfyinfo_s *vinfo, + const struct vfytag_s *tinfo, + rpmtd td, const char *origin, + struct rpmsinfo_s *sinfo) +@@ -140,6 +145,8 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo, + rpmRC rc = RPMRC_FAIL; + const void *data = NULL; + rpm_count_t dlen = 0; ++ uint8_t *pkt = NULL; ++ size_t pktlen = 0; + + *sinfo = vinfo->vi; /* struct assignment */ + sinfo->wrapped = (vinfo->sigh == 0); +@@ -193,6 +200,16 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo, + } + + if (sinfo->type == RPMSIG_SIGNATURE_TYPE) { ++ if (td->type == RPM_STRING_ARRAY_TYPE) { ++ if (rpmBase64Decode((const char *)data, (void **)&pkt, &pktlen)) { ++ rasprintf(&sinfo->msg, _("%s tag %u: invalid base64"), ++ origin, td->tag); ++ goto exit; ++ } ++ data = pkt; ++ dlen = pktlen; ++ } ++ + char *lints = NULL; + int ec = pgpPrtParams2(data, dlen, PGPTAG_SIGNATURE, &sinfo->sig, &lints); + if (ec) { +@@ -232,8 +249,10 @@ static void rpmsinfoInit(const struct vfyinfo_s *vinfo, + rc = RPMRC_OK; + + exit: ++ if (pkt && pkt != td->data) ++ free(pkt); + sinfo->rc = rc; +- return; ++ return rc; + } + + static void rpmsinfoFini(struct rpmsinfo_s *sinfo) +@@ -287,11 +306,16 @@ const char *rpmsinfoDescr(struct rpmsinfo_s *sinfo) + rangeName(sinfo->range), t); + free(t); + } else { +- rasprintf(&sinfo->descr, _("%s%s%s %s"), +- rangeName(sinfo->range), +- pgpValString(PGPVAL_PUBKEYALGO, sinfo->sigalgo), +- sinfo->alt ? " ALT" : "", +- _("signature")); ++ if (sinfo->sigalgo) { ++ rasprintf(&sinfo->descr, _("%s%s %s"), ++ rangeName(sinfo->range), ++ pgpValString(PGPVAL_PUBKEYALGO, sinfo->sigalgo), ++ _("signature")); ++ } else { ++ rasprintf(&sinfo->descr, _("%sOpenPGP %s"), ++ rangeName(sinfo->range), ++ _("signature")); ++ } + } + break; + } +@@ -327,7 +351,13 @@ static void rpmvsAppend(struct rpmvs_s *sis, hdrblob blob, + + if (!rpmsinfoDisabled(&vi->vi, sis->vsflags) && rc == RPMRC_OK) { + while (rpmtdNext(&td) >= 0) { +- rpmsinfoInit(vi, ti, &td, o, &sis->sigs[sis->nsigs]); ++ if (!rpmsinfoInit(vi, ti, &td, o, &sis->sigs[sis->nsigs])) { ++ /* Don't bother with v3/v4 sigs when v6 sigs exist */ ++ if (td.tag == RPMSIGTAG_OPENPGP) { ++ sis->vsflags |= (RPMVSF_NODSAHEADER|RPMVSF_NORSAHEADER); ++ sis->vsflags |= (RPMVSF_NODSA|RPMVSF_NORSA); ++ } ++ } + sis->nsigs++; + } + } else { +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index cd11d0919..3250c9114 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -13,6 +13,7 @@ + #include + #endif + ++#include + #include /* RPMSIGTAG & related */ + #include + #include +@@ -172,11 +173,12 @@ static rpmtd makeSigTag(int ishdr, uint8_t *pkt, size_t pktlen) + + /* Looks sane, create the tag data */ + sigtd = rpmtdNew(); ++ sigtd->tag = sigtag; ++ sigtd->flags |= RPMTD_ALLOCED; + sigtd->count = pktlen; + sigtd->data = memcpy(xmalloc(pktlen), pkt, pktlen);; +- sigtd->type = RPM_BIN_TYPE; + sigtd->tag = sigtag; +- sigtd->flags |= RPMTD_ALLOCED; ++ sigtd->type = RPM_BIN_TYPE; + + exit: + pgpDigParamsFree(sigp); +@@ -367,6 +369,7 @@ static void deleteSigs(Header sigh) + headerDel(sigh, RPMSIGTAG_DSA); + headerDel(sigh, RPMSIGTAG_RSA); + headerDel(sigh, RPMSIGTAG_PGP5); ++ headerDel(sigh, RPMSIGTAG_OPENPGP); + } + + static void deleteFileSigs(Header sigh) +@@ -377,41 +380,89 @@ static void deleteFileSigs(Header sigh) + headerDel(sigh, RPMSIGTAG_VERITYSIGNATUREALGO); + } + +-static int haveSignature(rpmtd sigtd, Header h) ++static pgpDigParams tdParams(rpmtd td) ++{ ++ pgpDigParams sig = NULL; ++ if (td->tag == RPMTAG_OPENPGP) { ++ uint8_t *pkt = NULL; ++ size_t pktlen = 0; ++ if (rpmBase64Decode(rpmtdGetString(td), (void **)&pkt, &pktlen) == 0) { ++ pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sig); ++ free(pkt); ++ } ++ } else { ++ pgpPrtParams((uint8_t *)td->data, td->count, PGPTAG_SIGNATURE, &sig); ++ } ++ return sig; ++} ++ ++static int haveSignature(rpmtd sigtd, Header sigh) + { +- pgpDigParams sig1 = NULL; +- pgpDigParams sig2 = NULL; + struct rpmtd_s oldtd; + int rc = 0; /* assume no */ ++ rpmTagVal tag = headerIsEntry(sigh, RPMSIGTAG_RESERVED) ? ++ RPMTAG_OPENPGP : sigtd->tag; + +- if (!headerGet(h, rpmtdTag(sigtd), &oldtd, HEADERGET_DEFAULT)) ++ if (!headerGet(sigh, tag, &oldtd, HEADERGET_DEFAULT)) + return rc; + ++ /* + pgpPrtParams(sigtd->data, sigtd->count, PGPTAG_SIGNATURE, &sig1); + while (rpmtdNext(&oldtd) >= 0 && rc == 0) { + pgpPrtParams(oldtd.data, oldtd.count, PGPTAG_SIGNATURE, &sig2); + if (pgpDigParamsCmp(sig1, sig2) == 0) ++ */ ++ pgpDigParams newsig = tdParams(sigtd); ++ while (rpmtdNext(&oldtd) >= 0 && rc == 0) { ++ pgpDigParams oldsig = tdParams(&oldtd); ++ if (pgpDigParamsCmp(newsig, oldsig) == 0) + rc = 1; +- sig2 = pgpDigParamsFree(sig2); ++ pgpDigParamsFree(oldsig); + } +- pgpDigParamsFree(sig1); ++ pgpDigParamsFree(newsig); + rpmtdFreeData(&oldtd); + + return rc; + } + +-static int putSignature(Header sigh, rpmtd sigtd) ++static int putSignature(Header sigh, rpmtd sigtd, int multisig) ++{ ++ int rc = 1; ++ if (multisig) { ++ char *b64 = rpmBase64Encode(sigtd->data, sigtd->count, 0); ++ char **arr = (char **)xmalloc(1 * sizeof(*arr)); ++ arr[0] = b64; ++ ++ struct rpmtd_s mtd = { ++ .tag = RPMSIGTAG_OPENPGP, ++ .type = RPM_STRING_ARRAY_TYPE, ++ .count = 1, ++ .data = arr, ++ .flags = RPMTD_ALLOCED|RPMTD_PTR_ALLOCED, ++ .ix = -1, ++ .size = 0, ++ }; ++ rc = (headerPut(sigh, &mtd, HEADERPUT_APPEND) == 0); ++ rpmtdFreeData(&mtd); ++ } else { ++ rc = (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0); ++ } ++ return rc; ++} ++ ++static int haveLegacySig(Header sigh) + { +- return (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0); ++ return headerIsEntry(sigh, RPMSIGTAG_RSA) || ++ headerIsEntry(sigh, RPMSIGTAG_DSA); + } + +-static int replaceSignature(Header sigh, rpmSignFlags flags, ++static int addSignature(Header sigh, rpmSignFlags flags, + sigTarget sigt_v3, sigTarget sigt_v4) + { + int rc = -1; + rpmtd sigtd = NULL; + +- /* Make the cheaper v4 signature first */ ++ /* Make a header signature */ + if ((sigtd = makeGPGSignature(1, sigt_v4)) == NULL) + goto exit; + +@@ -420,21 +471,28 @@ static int replaceSignature(Header sigh, rpmSignFlags flags, + rc = 1; + goto exit; + } +- /* Nuke all signature tags */ +- deleteSigs(sigh); +- +- if (putSignature(sigh, sigtd)) +- goto exit; + +- if (flags & RPMSIGN_FLAG_RPMV3) { +- rpmtdFree(sigtd); +- +- /* Assume the same signature test holds for v3 signature too */ +- if ((sigtd = makeGPGSignature(0, sigt_v3)) == NULL) ++ /* Add a v6 signature if requested */ ++ if (flags & RPMSIGN_FLAG_RPMV6) ++ if (putSignature(sigh, sigtd, 1)) + goto exit; + +- if (putSignature(sigh, sigtd)) ++ /* Add a v4 signature if requested */ ++ if (flags & RPMSIGN_FLAG_RPMV4) { ++ if (putSignature(sigh, sigtd, 0)) + goto exit; ++ ++ /* Only consider v3 signature if also adding v4 */ ++ if (flags & RPMSIGN_FLAG_RPMV3) { ++ rpmtdFree(sigtd); ++ ++ /* Assume the same signature test holds for v3 signature too */ ++ if ((sigtd = makeGPGSignature(0, sigt_v3)) == NULL) ++ goto exit; ++ ++ if (putSignature(sigh, sigtd, 0)) ++ goto exit; ++ } + } + + rc = 0; +@@ -625,6 +683,23 @@ static int rpmSign(const char *rpm, int deleting, int flags) + flags |= RPMSIGN_FLAG_RPMV3; + } + ++ /* Only v6 packages have this */ ++ if (headerIsEntry(sigh, RPMSIGTAG_RESERVED)) { ++ flags |= RPMSIGN_FLAG_RPMV6; ++ reserveTag = RPMSIGTAG_RESERVED; ++ /* v3 signatures are not welcome in v6 packages */ ++ if (flags & RPMSIGN_FLAG_RPMV3) { ++ rpmlog(RPMLOG_WARNING, ++ _("not generating v3 signature for v6 package: %s\n"), rpm); ++ flags &= ~RPMSIGN_FLAG_RPMV3; ++ } ++ } else { ++ flags |= RPMSIGN_FLAG_RPMV4; ++ /* Ensure only one legacy signature is added if adding v6 signatures */ ++ if ((flags & RPMSIGN_FLAG_RPMV6) && haveLegacySig(sigh)) ++ flags &= ~(RPMSIGN_FLAG_RPMV4|RPMSIGN_FLAG_RPMV3); ++ } ++ + origSigSize = headerSizeof(sigh, HEADER_MAGIC_YES); + unloadImmutableRegion(&sigh, RPMTAG_HEADERSIGNATURES); + +@@ -653,7 +728,10 @@ static int rpmSign(const char *rpm, int deleting, int flags) + sigt_v4 = sigt_v3; + sigt_v4.size = headerSizeof(h, HEADER_MAGIC_YES); + +- res = replaceSignature(sigh, flags, &sigt_v3, &sigt_v4); ++ if (flags & RPMSIGN_FLAG_RESIGN) ++ deleteSigs(sigh); ++ ++ res = addSignature(sigh, flags, &sigt_v3, &sigt_v4); + if (res != 0) { + if (res == 1) { + rpmlog(RPMLOG_WARNING, +@@ -667,10 +745,6 @@ static int rpmSign(const char *rpm, int deleting, int flags) + res = -1; + } + +- /* Only v6 packages have this */ +- if (headerIsEntry(h, RPMSIGTAG_RESERVED)) +- reserveTag = RPMSIGTAG_RESERVED; +- + /* Adjust reserved size for added/removed signatures */ + if (headerGet(sigh, reserveTag, &utd, HEADERGET_MINMEM)) { + unsigned newSize = headerSizeof(sigh, HEADER_MAGIC_YES); +diff --git a/tools/rpmsign.c b/tools/rpmsign.c +index 1b5f627ab..4bfec11cd 100644 +--- a/tools/rpmsign.c ++++ b/tools/rpmsign.c +@@ -43,7 +43,13 @@ static struct poptOption signOptsTable[] = { + #endif + { "rpmv3", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_RPMV3, +- N_("create rpm v3 header+payload signatures") }, ++ N_("request legacy rpm v3 header+payload signatures on v4 packages") }, ++ { "rpmv4", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), ++ &sargs.signflags, RPMSIGN_FLAG_RPMV4, ++ N_("request legacy rpm v4 header signatures on v6 packages") }, ++ { "rpmv6", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), ++ &sargs.signflags, RPMSIGN_FLAG_RPMV6, ++ N_("request rpm v6 header signature on v4 packages") }, + #ifdef WITH_IMAEVM + { "signfiles", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &sargs.signflags, RPMSIGN_FLAG_IMA, +@@ -207,8 +213,10 @@ int main(int argc, char *argv[]) + #endif + + switch (mode) { +- case MODE_ADDSIGN: + case MODE_RESIGN: ++ sargs.signflags |= RPMSIGN_FLAG_RESIGN; ++ /* fallthrough */ ++ case MODE_ADDSIGN: + ec = doSign(optCon, &sargs); + break; + case MODE_DELSIGN: +-- +2.50.1 + + +From 975f6d5feade692a5006197dbcec7ffab9a165c2 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 18 Nov 2024 15:50:06 +0200 +Subject: [PATCH 09/15] Add support for multiple OpenPGP signatures per + package, part 2/2 + +Add a tag extension for RPMTAG_OPENPGP (on top of the concrete tag) to +handle compatibility with v3/v4 signatures: the extension collects all +legacy signatures under the same umbrella so users don't need to query +multiple different tags, you just query for RPMTAG_OPENPGP to get all +them at once. Extend :pgpsig tag format to handle the new string +array/base64 variant. + +Update --info/-i query to use the extension and output all existing +signatures, one per line. The no-signature case of "Signature : (none)" +is preserved as-is to help backwards compatibility with scripts parsing +the output. + +Related: #3385 + +(backported from commit bea8f4557819449e6aec948dae192d71fe49d526) +--- + lib/formats.c | 24 +++++++++++++++++++++--- + lib/tagexts.c | 34 ++++++++++++++++++++++++++++++++++ + rpmpopt.in | 2 +- + 3 files changed, 56 insertions(+), 4 deletions(-) + +diff --git a/lib/formats.c b/lib/formats.c +index 303c12cbb..d4a6659c8 100644 +--- a/lib/formats.c ++++ b/lib/formats.c +@@ -340,12 +340,12 @@ exit: + } + + /* signature fingerprint and time formatting */ +-static char * pgpsigFormat(rpmtd td, char **emsg) ++static char * pgpsigFormatOne(uint8_t *pkt, size_t pktlen, char **emsg) + { + char * val = NULL; + pgpDigParams sigp = NULL; + +- if (pgpPrtParams(td->data, td->count, PGPTAG_SIGNATURE, &sigp)) { ++ if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) { + *emsg = xstrdup(_("(not an OpenPGP signature)")); + } else { + char dbuf[BUFSIZ]; +@@ -372,6 +372,24 @@ static char * pgpsigFormat(rpmtd td, char **emsg) + return val; + } + ++static char * pgpsigFormat(rpmtd td, char **emsg) ++{ ++ char *val = NULL; ++ if (rpmtdType(td) == RPM_BIN_TYPE) { ++ val = pgpsigFormatOne((uint8_t *)td->data, td->count, emsg); ++ } else if (rpmtdType(td) == RPM_STRING_ARRAY_TYPE) { ++ uint8_t *pkt = NULL; ++ size_t pktlen = 0; ++ if (rpmBase64Decode(rpmtdGetString(td), (void **)&pkt, &pktlen) == 0) { ++ val = pgpsigFormatOne(pkt, pktlen, emsg); ++ free(pkt); ++ } ++ } else { ++ *emsg = xstrdup(_("(invalid type)")); ++ } ++ return val; ++} ++ + /* dependency flags formatting */ + static char * depflagsFormat(rpmtd td, char **emsg) + { +@@ -510,7 +528,7 @@ static const struct headerFmt_s rpmHeaderFormats[] = { + { RPMTD_FORMAT_BASE64, "base64", + RPM_BINARY_CLASS, base64Format }, + { RPMTD_FORMAT_PGPSIG, "pgpsig", +- RPM_BINARY_CLASS, pgpsigFormat }, ++ RPM_NULL_CLASS, pgpsigFormat }, + { RPMTD_FORMAT_DEPFLAGS, "depflags", + RPM_NUMERIC_CLASS, depflagsFormat }, + { RPMTD_FORMAT_DEPTYPE, "deptype", +diff --git a/lib/tagexts.c b/lib/tagexts.c +index 713fa95d5..4261b6a82 100644 +--- a/lib/tagexts.c ++++ b/lib/tagexts.c +@@ -1011,6 +1011,39 @@ static int sysusersTag(Header h, rpmtd td, headerGetFlags hgflags) + return (td->count > 0); + } + ++static void trySigTag(Header h, rpmTagVal tag, ARGV_t *sigs) ++{ ++ struct rpmtd_s td; ++ if (headerGet(h, tag, &td, HEADERGET_ALLOC)) { ++ char *b64 = rpmBase64Encode((uint8_t *)td.data, td.count, 0); ++ if (b64) { ++ argvAdd(sigs, b64); ++ free(b64); ++ } ++ rpmtdFreeData(&td); ++ } ++} ++ ++static int openpgpTag(Header h, rpmtd td, headerGetFlags hgflags) ++{ ++ if (headerGet(h, RPMTAG_OPENPGP, td, HEADERGET_ALLOC)) ++ return 1; ++ ++ ARGV_t sigs = NULL; ++ trySigTag(h, RPMTAG_RSAHEADER, &sigs); ++ trySigTag(h, RPMTAG_DSAHEADER, &sigs); ++ trySigTag(h, RPMTAG_SIGPGP, &sigs); ++ trySigTag(h, RPMTAG_SIGGPG, &sigs); ++ ++ if (sigs) { ++ td->data = sigs; ++ td->count = argvCount(sigs); ++ td->type = RPM_STRING_ARRAY_TYPE; ++ td->flags = RPMTD_ALLOCED|RPMTD_PTR_ALLOCED; ++ } ++ return td->count != 0; ++} ++ + static int rpmformatTag(Header h, rpmtd td, headerGetFlags hgflags) + { + if (headerGet(h, RPMTAG_RPMFORMAT, td, HEADERGET_ALLOC)) +@@ -1067,6 +1100,7 @@ static const struct headerTagFunc_s rpmHeaderTagExtensions[] = { + { RPMTAG_CONFLICTNEVRS, conflictnevrsTag }, + { RPMTAG_FILENLINKS, filenlinksTag }, + { RPMTAG_SYSUSERS, sysusersTag }, ++ { RPMTAG_OPENPGP, openpgpTag }, + { RPMTAG_RPMFORMAT, rpmformatTag }, + { 0, NULL } + }; +diff --git a/rpmpopt.in b/rpmpopt.in +index 2e9ffb157..196909ce0 100644 +--- a/rpmpopt.in ++++ b/rpmpopt.in +@@ -96,7 +96,7 @@ Install Date: %|INSTALLTIME?{%{INSTALLTIME:date}}:{(not installed)}|\n\ + Group : %{GROUP}\n\ + Size : %{LONGSIZE}\n\ + %|LICENSE?{License : %{LICENSE}}|\n\ +-Signature : %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\n\ ++Signature :%|OPENPGP?{[\n %{OPENPGP:pgpsig}]}:{ (none)}|\n\ + Source RPM : %{SOURCERPM}\n\ + Build Date : %{BUILDTIME:date}\n\ + Build Host : %{BUILDHOST}\n\ +-- +2.50.1 + + +From 67b42940151f8ef7d882c8d58b6bb808836399d9 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 21 Nov 2024 13:30:20 +0200 +Subject: [PATCH 10/15] Use RPMTAG_RPMFORMAT for rpm format detection in the + signing code + +We now have a nice way to centrally get the format number, use it +in the signing code instead of the various ad-hoc methods added during +multi-signature development, and use 'rpmformat' as the variable name +for easy grepping. + +No functional changes. + +(cherry picked from commit ef5b53820a570e14bceb8435d8e417ee3563c1c5) +--- + sign/rpmgensig.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 3250c9114..7de52c22e 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -640,7 +640,8 @@ static int rpmSign(const char *rpm, int deleting, int flags) + struct sigTarget_s sigt_v4; + unsigned int origSigSize; + int insSig = 0; +- rpmTagVal reserveTag = RPMSIGTAG_RESERVEDSPACE; ++ int rpmformat = 0; ++ rpmTagVal reserveTag = 0; + + fprintf(stdout, "%s:\n", rpm); + +@@ -672,8 +673,10 @@ static int rpmSign(const char *rpm, int deleting, int flags) + goto exit; + } + +- if (!headerIsEntry(h, RPMTAG_HEADERIMMUTABLE)) { +- rpmlog(RPMLOG_ERR, _("Cannot sign RPM v3 packages\n")); ++ rpmformat = headerGetNumber(h, RPMTAG_RPMFORMAT); ++ ++ if (rpmformat < 4) { ++ rpmlog(RPMLOG_ERR, _("Cannot sign RPM v3 packages: %s\n"), rpm); + goto exit; + } + +@@ -683,8 +686,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + flags |= RPMSIGN_FLAG_RPMV3; + } + +- /* Only v6 packages have this */ +- if (headerIsEntry(sigh, RPMSIGTAG_RESERVED)) { ++ if (rpmformat >= 6) { + flags |= RPMSIGN_FLAG_RPMV6; + reserveTag = RPMSIGTAG_RESERVED; + /* v3 signatures are not welcome in v6 packages */ +@@ -695,6 +697,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + } + } else { + flags |= RPMSIGN_FLAG_RPMV4; ++ reserveTag = RPMSIGTAG_RESERVEDSPACE; + /* Ensure only one legacy signature is added if adding v6 signatures */ + if ((flags & RPMSIGN_FLAG_RPMV6) && haveLegacySig(sigh)) + flags &= ~(RPMSIGN_FLAG_RPMV4|RPMSIGN_FLAG_RPMV3); +-- +2.50.1 + + +From 6ff3d9fccdd1b9e5b05b5e11b083f7f72b4d19ef Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Sun, 22 Jun 2025 15:02:13 +0200 +Subject: [PATCH 11/15] Allow all data classes for RPM_NULL_CLASS fortmats + +--- + lib/formats.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/formats.c b/lib/formats.c +index d4a6659c8..9174710d5 100644 +--- a/lib/formats.c ++++ b/lib/formats.c +@@ -603,7 +603,7 @@ char *rpmHeaderFormatCall(headerFmt fmt, rpmtd td) + char *ret = NULL; + char *err = NULL; + +- if (fmt->class != RPM_ANY_CLASS && rpmtdClass(td) != fmt->class) ++ if (fmt->class != RPM_NULL_CLASS && fmt->class != RPM_ANY_CLASS && rpmtdClass(td) != fmt->class) + err = xstrdup(classEr(fmt->class)); + else + ret = fmt->func(td, &err); +-- +2.50.1 + + +From 2302c156d3af8c45e19bf95e69ffab3dd0a25a1f Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 14 May 2025 14:29:34 +0300 +Subject: [PATCH 12/15] Bump rpm-sequoia requirement to 1.8 for OpenPGP v6 + support + +For OpenPGP v6 we need sequoia-sq >= 1.3 and rpm-sequoia >= 1.8 in the +bare minimum, the latter is only available from rawhide so we need to +pull it from there. This also requires a newer crypto-policies package +than is available on any released version to permit ed25519 and x25519, +otherwise we'll get funky "denied by policy" errors when signing. +Talk about bleeding edge! + +(cherry picked from commit 8b1de0bd9be862a27fc66e5d9bcf5936ac4c8e7f) +--- + rpmio/CMakeLists.txt | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/rpmio/CMakeLists.txt b/rpmio/CMakeLists.txt +index 062b680cc..aec48966c 100644 +--- a/rpmio/CMakeLists.txt ++++ b/rpmio/CMakeLists.txt +@@ -13,8 +13,15 @@ target_include_directories(librpmio PRIVATE + ${Intl_INCLUDE_DIRS} + ) + +-if (WITH_INTERNAL_OPENPGP) ++ ++if (WITH_SEQUOIA) ++ pkg_check_modules(RPMSEQUOIA REQUIRED IMPORTED_TARGET rpm-sequoia>=1.8.0) ++ target_sources(librpmio PRIVATE rpmpgp_sequoia.c) ++ target_link_libraries(librpmio PRIVATE PkgConfig::RPMSEQUOIA) ++else() ++ if (WITH_INTERNAL_OPENPGP) + target_sources(librpmio PRIVATE rpmpgp_internal.h rpmpgp_internal.c) ++ + if (WITH_OPENSSL) + find_package(OpenSSL 1.0.2 REQUIRED) + target_sources(librpmio PRIVATE digest_openssl.c) +@@ -24,10 +31,7 @@ if (WITH_INTERNAL_OPENPGP) + target_sources(librpmio PRIVATE digest_libgcrypt.c) + target_link_libraries(librpmio PRIVATE PkgConfig::LIBGCRYPT) + endif() +-else() +- pkg_check_modules(RPMSEQUOIA REQUIRED IMPORTED_TARGET rpm-sequoia>=1.4.0) +- target_sources(librpmio PRIVATE rpmpgp_sequoia.c) +- target_link_libraries(librpmio PRIVATE PkgConfig::RPMSEQUOIA) ++ endif() + endif() + + set_target_properties(librpmio PROPERTIES +-- +2.50.1 + + +From 559c751bdc06f696b5b8aaaaee2a78c0265ba19e Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 26 May 2025 12:04:47 +0300 +Subject: [PATCH 13/15] Fix some cases issues in rpmdump signature tag names + +(cherry picked from commit 1dccba4120b5d873d89eff07b2ea8eef7b7f287c) +--- + tools/rpmdump.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tools/rpmdump.c b/tools/rpmdump.c +index 329ce5978..e8ba361ff 100644 +--- a/tools/rpmdump.c ++++ b/tools/rpmdump.c +@@ -45,9 +45,9 @@ static const char *sigTagName(uint32_t tag) + case RPMSIGTAG_LONGARCHIVESIZE: return "Longarchivesize"; + case RPMSIGTAG_SHA256: return "Sha256"; + case RPMSIGTAG_FILESIGNATURES: return "Filesignatures"; +- case RPMSIGTAG_FILESIGNATURELENGTH: return "filesignaturelength"; +- case RPMSIGTAG_VERITYSIGNATURES: return "veritysignatures"; +- case RPMSIGTAG_VERITYSIGNATUREALGO: return "veritysignaturealgo"; ++ case RPMSIGTAG_FILESIGNATURELENGTH: return "Filesignaturelength"; ++ case RPMSIGTAG_VERITYSIGNATURES: return "Veritysignatures"; ++ case RPMSIGTAG_VERITYSIGNATUREALGO: return "Veritysignaturealgo"; + case RPMSIGTAG_RESERVED: return "Reserved"; + default: + break; +-- +2.50.1 + + +From 5f85c34dee3fc23289be12881db68cbe6f7cc380 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 26 May 2025 12:05:33 +0300 +Subject: [PATCH 14/15] Teach rpmdump about RPMSIGTAG_OPENPGP + +Should've been in commit 5630cf4bcbaae85d1b53e7a57ba6c7ed86b62e7d + +(cherry picked from commit bb028ef5de36b29f4bdd6480269e26455e115a8d) +--- + tools/rpmdump.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/rpmdump.c b/tools/rpmdump.c +index e8ba361ff..8891b8e36 100644 +--- a/tools/rpmdump.c ++++ b/tools/rpmdump.c +@@ -48,6 +48,7 @@ static const char *sigTagName(uint32_t tag) + case RPMSIGTAG_FILESIGNATURELENGTH: return "Filesignaturelength"; + case RPMSIGTAG_VERITYSIGNATURES: return "Veritysignatures"; + case RPMSIGTAG_VERITYSIGNATUREALGO: return "Veritysignaturealgo"; ++ case RPMSIGTAG_OPENPGP: return "Openpgp"; + case RPMSIGTAG_RESERVED: return "Reserved"; + default: + break; +-- +2.50.1 + + +From 0755de004e095a62e3bbc18e8ae9599c9a9a18bd Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 15 May 2025 14:50:57 +0300 +Subject: [PATCH 15/15] Fix rpm v6 signing with algorithms not supported by rpm + v4 signatures + +Adding an rpm v6 signature fails with a message such as +"error: Unsupported OpenPGP pubkey algorithm 27" if the algorithm +isn't supported by rpm v4 signatures. + +The issue here seems simple enough: makeSigTag() assumes there'll always +be a legacy tag to map to, but that's not the case with new algorithms +such as those added in RFC-9580. Only, this tiny thing causes a bit of an +avalance: we need to move the tag decision logic to putSignature(), but to +do that we also need to move the check for identical signatures there, +and to do that we need to pay more attention to putSignature() retuns. +And then we can finally make the decisions we need, where we need them: + +When adding an rpm v4 signature, suppress the error from an "unknown" +algorithm if we already added an rpm v6 signature for it in the same run. + +Add tests to the extent we can, rpm-sequoia 1.8 doesn't fully handle +OpenPGP v6 it seems: +https://github.com/rpm-software-management/rpm-sequoia/issues/87 + +Fixes: #3752 +(cherry picked from commit ad114b0174c26fa101ce9bbf82930b2c2e421a09) +--- + sign/rpmgensig.c | 95 +++++++++++++++++++++++++++--------------------- + 1 file changed, 54 insertions(+), 41 deletions(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 7de52c22e..a96bf7ed0 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -129,13 +129,11 @@ exit: + } + + /* Wrap a raw signature in an rpmtd and sanity check, return NULL on fail */ +-static rpmtd makeSigTag(int ishdr, uint8_t *pkt, size_t pktlen) ++static rpmtd makeSigTag(uint8_t *pkt, size_t pktlen) + { + pgpDigParams sigp = NULL; +- rpmTagVal sigtag; + rpmtd sigtd = NULL; + unsigned int hash_algo; +- unsigned int pubkey_algo; + int ver; + + if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) { +@@ -149,22 +147,6 @@ static rpmtd makeSigTag(int ishdr, uint8_t *pkt, size_t pktlen) + goto exit; + } + +- pubkey_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO); +- switch (pubkey_algo) { +- case PGPPUBKEYALGO_DSA: +- case PGPPUBKEYALGO_EDDSA: +- sigtag = ishdr ? RPMSIGTAG_DSA : RPMSIGTAG_GPG; +- break; +- case PGPPUBKEYALGO_RSA: +- sigtag = ishdr ? RPMSIGTAG_RSA : RPMSIGTAG_PGP; +- break; +- default: +- rpmlog(RPMLOG_ERR, _("Unsupported OpenPGP pubkey algorithm %u\n"), +- pubkey_algo); +- goto exit; +- break; +- } +- + ver = pgpDigParamsVersion(sigp); + if (ver < 4) { + rpmlog(RPMLOG_WARNING, _("Deprecated OpenPGP signature version %d\n"), +@@ -173,12 +155,12 @@ static rpmtd makeSigTag(int ishdr, uint8_t *pkt, size_t pktlen) + + /* Looks sane, create the tag data */ + sigtd = rpmtdNew(); +- sigtd->tag = sigtag; + sigtd->flags |= RPMTD_ALLOCED; + sigtd->count = pktlen; + sigtd->data = memcpy(xmalloc(pktlen), pkt, pktlen);; +- sigtd->tag = sigtag; + sigtd->type = RPM_BIN_TYPE; ++ /* Hack: the actual tag gets decided in putSignature() based on this */ ++ sigtd->tag = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO); + + exit: + pgpDigParamsFree(sigp); +@@ -315,7 +297,7 @@ exit_nowait: + } + + /* Generate an OpenPGP signature(s) for a target */ +-static rpmtd makeGPGSignature(int ishdr, sigTarget sigt) ++static rpmtd makeGPGSignature(sigTarget sigt) + { + char * sigfile = rstrscat(NULL, sigt->fileName, ".sig", NULL); + struct stat st; +@@ -353,7 +335,7 @@ static rpmtd makeGPGSignature(int ishdr, sigTarget sigt) + rpmlog(RPMLOG_DEBUG, "Got %zd bytes of OpenPGP sig\n", pktlen); + + /* Parse the signature, change signature tag as appropriate. */ +- sigtd = makeSigTag(ishdr, pkt, pktlen); ++ sigtd = makeSigTag(pkt, pktlen); + exit: + (void) unlink(sigfile); + free(sigfile); +@@ -400,10 +382,8 @@ static int haveSignature(rpmtd sigtd, Header sigh) + { + struct rpmtd_s oldtd; + int rc = 0; /* assume no */ +- rpmTagVal tag = headerIsEntry(sigh, RPMSIGTAG_RESERVED) ? +- RPMTAG_OPENPGP : sigtd->tag; + +- if (!headerGet(sigh, tag, &oldtd, HEADERGET_DEFAULT)) ++ if (!headerGet(sigh, sigtd->tag, &oldtd, HEADERGET_DEFAULT)) + return rc; + + /* +@@ -425,9 +405,11 @@ static int haveSignature(rpmtd sigtd, Header sigh) + return rc; + } + +-static int putSignature(Header sigh, rpmtd sigtd, int multisig) ++static int putSignature(Header sigh, rpmtd sigtd, int multisig, int ishdr, ++ rpmSignFlags flags) + { +- int rc = 1; ++ int rc = -1; ++ + if (multisig) { + char *b64 = rpmBase64Encode(sigtd->data, sigtd->count, 0); + char **arr = (char **)xmalloc(1 * sizeof(*arr)); +@@ -442,11 +424,48 @@ static int putSignature(Header sigh, rpmtd sigtd, int multisig) + .ix = -1, + .size = 0, + }; +- rc = (headerPut(sigh, &mtd, HEADERPUT_APPEND) == 0); ++ if (haveSignature(&mtd, sigh)) { ++ rc = 1; ++ } else { ++ rc = (headerPut(sigh, &mtd, HEADERPUT_APPEND) == 0) ? -1 : 0; ++ } + rpmtdFreeData(&mtd); + } else { +- rc = (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0); ++ unsigned int pubkey_algo = sigtd->tag; ++ uint32_t sigtag = 0; ++ switch (pubkey_algo) { ++ case PGPPUBKEYALGO_DSA: ++ case PGPPUBKEYALGO_ECDSA: ++ case PGPPUBKEYALGO_EDDSA: ++ sigtag = ishdr ? RPMSIGTAG_DSA : RPMSIGTAG_GPG; ++ break; ++ case PGPPUBKEYALGO_RSA: ++ sigtag = ishdr ? RPMSIGTAG_RSA : RPMSIGTAG_PGP; ++ break; ++ default: ++ break; ++ } ++ ++ if (sigtag) { ++ sigtd->tag = sigtag; ++ if (haveSignature(sigtd, sigh)) { ++ rc = 1; ++ } else { ++ rc = (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) ? -1 : 0; ++ } ++ sigtd->tag = pubkey_algo; ++ } else { ++ /* If we did a v6 signature, we can ignore the error here */ ++ if (flags & RPMSIGN_FLAG_RPMV6) { ++ rc = 0; ++ } else { ++ rpmlog(RPMLOG_ERR, ++ _("Unsupported OpenPGP pubkey algorithm %u for rpm v3/v4 signatures\n"), ++ pubkey_algo); ++ } ++ } + } ++ + return rc; + } + +@@ -463,23 +482,17 @@ static int addSignature(Header sigh, rpmSignFlags flags, + rpmtd sigtd = NULL; + + /* Make a header signature */ +- if ((sigtd = makeGPGSignature(1, sigt_v4)) == NULL) +- goto exit; +- +- /* See if we already have a signature by the same key and parameters */ +- if (haveSignature(sigtd, sigh)) { +- rc = 1; ++ if ((sigtd = makeGPGSignature(sigt_v4)) == NULL) + goto exit; +- } + + /* Add a v6 signature if requested */ + if (flags & RPMSIGN_FLAG_RPMV6) +- if (putSignature(sigh, sigtd, 1)) ++ if ((rc = putSignature(sigh, sigtd, 1, 1, flags))) + goto exit; + + /* Add a v4 signature if requested */ + if (flags & RPMSIGN_FLAG_RPMV4) { +- if (putSignature(sigh, sigtd, 0)) ++ if ((rc = putSignature(sigh, sigtd, 0, 1, flags))) + goto exit; + + /* Only consider v3 signature if also adding v4 */ +@@ -487,10 +500,10 @@ static int addSignature(Header sigh, rpmSignFlags flags, + rpmtdFree(sigtd); + + /* Assume the same signature test holds for v3 signature too */ +- if ((sigtd = makeGPGSignature(0, sigt_v3)) == NULL) ++ if ((sigtd = makeGPGSignature(sigt_v3)) == NULL) + goto exit; + +- if (putSignature(sigh, sigtd, 0)) ++ if ((rc = putSignature(sigh, sigtd, 0, 0, flags))) + goto exit; + } + } +-- +2.50.1 + + +From 4c3f4fa2c1c514aa05a15c0756495c1b762e5f17 Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Fri, 25 Jul 2025 17:35:31 +0200 +Subject: [PATCH] Fix --addsign not replacing existing V4 signature + +Historically, --addsign has been an alias to --resign, so don't break +that behavior in RHEL. Do keep the original warning when adding an +identical signature, though, which means we can't just enable +RPMSIGN_FLAG_RESIGN internally in rpmSign() since it has to happen +*after* the haveSignature() check. + +This patch isn't yet upstreamed, although it might need to be since +otherwise we effectively add a duplicate V4 tag in the above case, which +is wrong by itself (and just happens to be gracefully ignored by RPM +during verification). This will be done later, now let's just fix the +RHEL regression. + +Update the man page accordingly, and add a bunch of tests to go with. +--- + docs/man/rpmsign.8.md | 2 ++ + sign/rpmgensig.c | 6 ++++++ + tools/rpmsign.c | 2 +- + 3 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/docs/man/rpmsign.8.md b/docs/man/rpmsign.8.md +index d6deebec2..a4b98ce38 100644 +--- a/docs/man/rpmsign.8.md ++++ b/docs/man/rpmsign.8.md +@@ -33,6 +33,8 @@ DESCRIPTION + **rpmsign** **\--addsign** generates and inserts a new OpenPGP signature + for each *PACKAGE\_FILE* given unless a signature with identical + parameters already exists, in which case no action is taken. ++When signing a V4 package, and **\--rpmv6** is not used, any existing ++signatures are replaced, as if **\--resign** was used instead. + Arbitrary number of V6 signatures can be added. + + **rpmsign** **\--resign** generates and inserts a new OpenPGP signature +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index 4596b5116..98090521b 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -451,6 +451,12 @@ static int putSignature(Header sigh, rpmtd sigtd, int multisig, int ishdr, + if (haveSignature(sigtd, sigh)) { + rc = 1; + } else { ++ /* Only one legacy signature is allowed */ ++ if (ishdr && !(flags & RPMSIGN_FLAG_RPMV6)) ++ /* Preserve historical behavior */ ++ deleteSigs(sigh); ++ else ++ headerDel(sigh, sigtd->tag); + rc = (headerPut(sigh, sigtd, HEADERPUT_DEFAULT) == 0) ? -1 : 0; + } + sigtd->tag = pubkey_algo; +diff --git a/tools/rpmsign.c b/tools/rpmsign.c +index 4bfec11cd..41bb89175 100644 +--- a/tools/rpmsign.c ++++ b/tools/rpmsign.c +@@ -34,7 +34,7 @@ static struct poptOption signOptsTable[] = { + { "addsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_ADDSIGN, + N_("sign package(s)"), NULL }, + { "resign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_RESIGN, +- N_("sign package(s) (identical to --addsign)"), NULL }, ++ N_("sign package(s), replacing any existing signatures"), NULL }, + { "delsign", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELSIGN, + N_("delete package signatures"), NULL }, + #if defined(WITH_IMAEVM) || defined(WITH_FSVERITY) +-- +2.50.1 + + +diff -up rpm-4.19.1.1/docs/man/rpmsign.8.orig rpm-4.19.1.1/docs/man/rpmsign.8 +--- rpm-4.19.1.1/docs/man/rpmsign.8.orig 2025-07-31 17:08:24.649737856 +0200 ++++ rpm-4.19.1.1/docs/man/rpmsign.8 2025-07-31 17:08:59.012502800 +0200 +@@ -1,82 +1,103 @@ +-.\" Automatically generated by Pandoc 3.1.3 ++.\" Automatically generated by Pandoc 3.1.11.1 + .\" +-.\" Define V font for inline verbatim, using C font in formats +-.\" that render this, and otherwise B font. +-.ie "\f[CB]x\f[]"x" \{\ +-. ftr V B +-. ftr VI BI +-. ftr VB B +-. ftr VBI BI +-.\} +-.el \{\ +-. ftr V CR +-. ftr VI CI +-. ftr VB CB +-. ftr VBI CBI +-.\} + .TH "RPMSIGN" "8" "Red Hat, Inc" "" "" +-.hy + .SH NAME +-.PP +-rpmsign - RPM Package Signing ++rpmsign \- RPM Package Signing + .SH SYNOPSIS + .SS SIGNING PACKAGES: ++\f[B]rpmsign\f[R] \f[B]\-\-addsign|\-\-resign\f[R] ++[\f[B]rpmsign\-options\f[R]] \f[I]PACKAGE_FILE ...\f[R] + .PP +-\f[B]rpmsign\f[R] \f[B]--addsign|--resign\f[R] +-[\f[B]rpmsign-options\f[R]] \f[I]PACKAGE_FILE ...\f[R] +-.PP +-\f[B]rpmsign\f[R] \f[B]--delsign\f[R] \f[I]PACKAGE_FILE ...\f[R] ++\f[B]rpmsign\f[R] \f[B]\-\-delsign\f[R] \f[I]PACKAGE_FILE ...\f[R] + .PP +-\f[B]rpmsign\f[R] \f[B]--delfilesign\f[R] \f[I]PACKAGE_FILE ...\f[R] +-.SS rpmsign-options +-.PP +-[\f[B]--rpmv3\f[R]] [\f[B]--fskpath \f[R]\f[I]KEY\f[R]] +-[\f[B]--signfiles\f[R]] ++\f[B]rpmsign\f[R] \f[B]\-\-delfilesign\f[R] \f[I]PACKAGE_FILE ...\f[R] ++.SS rpmsign\-options ++[\f[B]\-\-rpmv3\f[R]] [\f[B]\-\-rpmv4\f[R]] [\f[B]\-\-rpmv6\f[R]] ++[\f[B]\-\-fskpath \f[R]\f[I]KEY\f[R]] [\f[B]\-\-signfiles\f[R]] + .SH DESCRIPTION +-.PP +-Both of the \f[B]--addsign\f[R] and \f[B]--resign\f[R] options generate +-and insert new signatures for each package \f[I]PACKAGE_FILE\f[R] given, +-replacing any existing signatures. +-There are two options for historical reasons, there is no difference in +-behavior currently. ++\f[B]rpmsign\f[R] \f[B]\-\-addsign\f[R] generates and inserts a new ++OpenPGP signature for each \f[I]PACKAGE_FILE\f[R] given unless a ++signature with identical parameters already exists, in which case no ++action is taken. ++When signing a V4 package, and \f[B]\-\-rpmv6\f[R] is not used, any ++existing signatures are replaced, as if \f[B]\-\-resign\f[R] was used ++instead. ++Arbitrary number of V6 signatures can be added. ++.PP ++\f[B]rpmsign\f[R] \f[B]\-\-resign\f[R] generates and inserts a new ++OpenPGP signature for each \f[I]PACKAGE_FILE\f[R], replacing any and all ++previous signatures. + .PP + To create a signature rpmsign needs to verify the package\[aq]s + checksum. +-As a result packages with a MD5/SHA1 checksums cannot be signed in FIPS ++As a result V4 packages with MD5/SHA1 checksums cannot be signed in FIPS + mode. + .PP +-\f[B]rpmsign\f[R] \f[B]--delsign\f[R] \f[I]PACKAGE_FILE ...\f[R] ++\f[B]rpmsign\f[R] \f[B]\-\-delsign\f[R] \f[I]PACKAGE_FILE ...\f[R] + .PP +-Delete all signatures from each package \f[I]PACKAGE_FILE\f[R] given. ++Delete all OpenPGP signatures from each package \f[I]PACKAGE_FILE\f[R] ++given. + .PP +-\f[B]rpmsign\f[R] \f[B]--delfilesign\f[R] \f[I]PACKAGE_FILE ...\f[R] ++\f[B]rpmsign\f[R] \f[B]\-\-delfilesign\f[R] \f[I]PACKAGE_FILE ...\f[R] + .PP + Delete all IMA and fsverity file signatures from each package + \f[I]PACKAGE_FILE\f[R] given. + .SS SIGN OPTIONS + .TP +-\f[B]--rpmv3\f[R] +-Force RPM V3 header+payload signature addition. +-These are expensive and redundant baggage on packages where a separate +-payload digest exists (packages built with rpm >= 4.14). ++\f[B]\-\-rpmv3\f[R] ++Request RPM V3 header+payload signature addition on V4 packages. ++These signatures are expensive and redundant baggage on packages where a ++separate payload digest exists (packages built with rpm >= 4.14). + Rpmsign will automatically detect the need for V3 signatures, but this +-option can be used to force their creation if the packages must be fully +-signature verifiable with rpm < 4.14 or other interoperability reasons. ++option can be used to request their creation if the packages must be ++fully signature verifiable with rpm < 4.14 or other interoperability ++reasons. ++.RS ++.PP ++Has no effect when signing V6 packages. ++.RE ++.TP ++\f[B]\-\-rpmv4\f[R] ++Request RPM V4 header signature addition on V6 packages. ++Useful for making V6 packages signature verifiable with rpm 4.x ++versions. ++.RS ++.PP ++V4 compatibility signatures are only ever added if the signing algorithm ++is one of those known to V4: RSA, EcDSA, EdDSA (and original DSA). ++Only one V4 signature can be present in a package, so this is added only ++on the first \f[B]\-\-addsign\f[R] with a V4 compatible algorithm, and ++ignored otherwise. ++.PP ++Has no effect when signing V4 packages. ++.RE ++.TP ++\f[B]\-\-rpmv6\f[R] ++Request RPM V6 header signature addition on V4 packages. ++.RS ++.PP ++This generally always succeeds as there can be arbitrary number of V6 ++signatures on a package. ++A V3/V4 compatibility signatures are added usign the same logic as ++\f[B]\-\-rpmv4\f[R] on a V6 package. ++.PP ++Has no effect when signing V6 packages. ++.RE + .TP +-\f[B]--fskpath \f[R]\f[I]KEY\f[R] +-Used with \f[B]--signfiles\f[R], use file signing key \f[I]Key\f[R]. ++\f[B]\-\-fskpath \f[R]\f[I]KEY\f[R] ++Used with \f[B]\-\-signfiles\f[R], use file signing key \f[I]Key\f[R]. + .TP +-\f[B]--certpath \f[R]\f[I]CERT\f[R] +-Used with \f[B]--signverity\f[R], use file signing certificate ++\f[B]\-\-certpath \f[R]\f[I]CERT\f[R] ++Used with \f[B]\-\-signverity\f[R], use file signing certificate + \f[I]Cert\f[R]. + .TP +-\f[B]--verityalgo \f[R]\f[I]ALG\f[R] +-Used with \f[B]--signverity\f[R], to specify the signing algorithm. ++\f[B]\-\-verityalgo \f[R]\f[I]ALG\f[R] ++Used with \f[B]\-\-signverity\f[R], to specify the signing algorithm. + sha256 and sha512 are supported, with sha256 being the default if this + argument is not specified. + This can also be specified with the macro %_verity_algorithm + .TP +-\f[B]--signfiles\f[R] ++\f[B]\-\-signfiles\f[R] + Sign package files. + The macro \f[B]%_binary_filedigest_algorithm\f[R] must be set to a + supported algorithm before building the package. +@@ -84,17 +105,17 @@ The supported algorithms are SHA1, SHA25 + represented as 2, 8, 9, and 10 respectively. + The file signing key (RSA private key) must be set before signing the + package, it can be configured on the command line with +-\f[B]--fskpath\f[R] or the macro %_file_signing_key. ++\f[B]\-\-fskpath\f[R] or the macro %_file_signing_key. + .TP +-\f[B]--signverity\f[R] ++\f[B]\-\-signverity\f[R] + Sign package files with fsverity signatures. + The file signing key (RSA private key) and the signing certificate must + be set before signing the package. +-The key can be configured on the command line with \f[B]--fskpath\f[R] ++The key can be configured on the command line with \f[B]\-\-fskpath\f[R] + or the macro %_file_signing_key, and the cert can be configured on the +-command line with \f[B]--certpath\f[R] or the macro %_file_signing_cert. ++command line with \f[B]\-\-certpath\f[R] or the macro ++%_file_signing_cert. + .SS USING GPG TO SIGN PACKAGES +-.PP + In order to sign packages using GPG, \f[B]rpm\f[R] must be configured to + run GPG and be able to find a key ring with the appropriate keys. + By default, \f[B]rpm\f[R] uses the same conventions as GPG to find key +@@ -115,38 +136,33 @@ For example, to be able to use GPG to si + located in \f[I]/etc/rpm/.gpg\f[R] using the executable + \f[I]/usr/bin/gpg\f[R] you would include + .IP +-.nf +-\f[C] ++.EX + %_gpg_path /etc/rpm/.gpg + %_gpg_name John Doe + %__gpg /usr/bin/gpg +-\f[R] +-.fi ++.EE + .PP + in a macro configuration file. +-Use \f[I]/etc/rpm/macros\f[R] for per-system configuration and +-\f[I]\[ti]/.rpmmacros\f[R] for per-user configuration. ++Use \f[I]/etc/rpm/macros\f[R] for per\-system configuration and ++\f[I]\[ti]/.rpmmacros\f[R] for per\-user configuration. + Typically it\[aq]s sufficient to set just %_gpg_name. + .SH SEE ALSO +-.PP + \f[B]popt\f[R](3), \f[B]rpm\f[R](8), \f[B]rpmdb\f[R](8), + \f[B]rpmkeys\f[R](8), \f[B]rpm2cpio\f[R](8), \f[B]rpmbuild\f[R](8), + \f[B]rpmspec\f[R](8) + .PP +-\f[B]rpmsign --help\f[R] - as rpm supports customizing the options via +-popt aliases it\[aq]s impossible to guarantee that what\[aq]s described +-in the manual matches what\[aq]s available. ++\f[B]rpmsign \-\-help\f[R] \- as rpm supports customizing the options ++via popt aliases it\[aq]s impossible to guarantee that what\[aq]s ++described in the manual matches what\[aq]s available. + .PP + \f[B]http://www.rpm.org/ \f[R] + .SH AUTHORS + .IP +-.nf +-\f[C] ++.EX + Marc Ewing + Jeff Johnson + Erik Troan + Panu Matilainen + Fionnuala Gunter + Jes Sorensen +-\f[R] +-.fi ++.EE diff --git a/rpm-4.19.x-pqc-algo.patch b/rpm-4.19.x-pqc-algo.patch new file mode 100644 index 0000000..d7644e4 --- /dev/null +++ b/rpm-4.19.x-pqc-algo.patch @@ -0,0 +1,668 @@ +From 9eac03ec09efca60810ea2b655f0f25edc86890b Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 3 Dec 2024 10:44:46 +0200 +Subject: [PATCH 1/8] Add support for SHA3-256 and SHA3-512 + +Sequoia doesn't yet support SHA3 so we need to skip it in the default +CI tests. Tests verified locally with libgrypt and openssl builds. + +Fixes: #3436 +(backported from commit 9344367edf654fb41a136793b68cea9abb892fb9) +--- + include/rpm/rpmcrypto.h | 11 +++++++---- + include/rpm/rpmpgp.h | 11 +++++++---- + macros.in | 2 ++ + rpmio/digest_libgcrypt.c | 8 ++++++++ + rpmio/digest_openssl.c | 6 ++++++ + 5 files changed, 30 insertions(+), 8 deletions(-) + +diff --git a/include/rpm/rpmcrypto.h b/include/rpm/rpmcrypto.h +index 69d329f37..d8e31a222 100644 +--- a/include/rpm/rpmcrypto.h ++++ b/include/rpm/rpmcrypto.h +@@ -23,10 +23,13 @@ typedef enum rpmHashAlgo_e { + RPM_HASH_MD2 = 5, /*!< MD2 */ + RPM_HASH_TIGER192 = 6, /*!< TIGER192 */ + RPM_HASH_HAVAL_5_160 = 7, /*!< HAVAL-5-160 */ +- RPM_HASH_SHA256 = 8, /*!< SHA256 */ +- RPM_HASH_SHA384 = 9, /*!< SHA384 */ +- RPM_HASH_SHA512 = 10, /*!< SHA512 */ +- RPM_HASH_SHA224 = 11, /*!< SHA224 */ ++ RPM_HASH_SHA256 = 8, /*!< SHA2-256 */ ++ RPM_HASH_SHA384 = 9, /*!< SHA2-384 */ ++ RPM_HASH_SHA512 = 10, /*!< SHA2-512 */ ++ RPM_HASH_SHA224 = 11, /*!< SHA2-224 */ ++ RPM_HASH_SHA3_256 = 12, /*!< SHA3-256 */ ++ /*!< reserved */ ++ RPM_HASH_SHA3_512 = 14, /*!< SHA3-512 */ + } rpmHashAlgo; + + /** \ingroup rpmcrypto +diff --git a/include/rpm/rpmpgp.h b/include/rpm/rpmpgp.h +index a7eecbebf..1f356f412 100644 +--- a/include/rpm/rpmpgp.h ++++ b/include/rpm/rpmpgp.h +@@ -266,10 +266,13 @@ typedef enum pgpHashAlgo_e { + PGPHASHALGO_MD2 = 5, /*!< MD2 */ + PGPHASHALGO_TIGER192 = 6, /*!< TIGER192 */ + PGPHASHALGO_HAVAL_5_160 = 7, /*!< HAVAL-5-160 */ +- PGPHASHALGO_SHA256 = 8, /*!< SHA256 */ +- PGPHASHALGO_SHA384 = 9, /*!< SHA384 */ +- PGPHASHALGO_SHA512 = 10, /*!< SHA512 */ +- PGPHASHALGO_SHA224 = 11, /*!< SHA224 */ ++ PGPHASHALGO_SHA256 = 8, /*!< SHA2-256 */ ++ PGPHASHALGO_SHA384 = 9, /*!< SHA2-384 */ ++ PGPHASHALGO_SHA512 = 10, /*!< SHA2-512 */ ++ PGPHASHALGO_SHA224 = 11, /*!< SHA2-224 */ ++ PGPHASHALGO_SHA3_256 = 12, /*!< SHA3-256 */ ++ /*!< 13 reserved */ ++ PGPHASHALGO_SHA3_512 = 14, /*!< SHA3-256 */ + } pgpHashAlgo; + + /** \ingroup rpmpgp +diff --git a/macros.in b/macros.in +index 8240e9613..5534f1ed7 100644 +--- a/macros.in ++++ b/macros.in +@@ -391,6 +391,8 @@ Supplements: (%{name} = %{version}-%{release} and langpacks-%{1})\ + # 8 SHA256 (default) + # 9 SHA384 + # 10 SHA512 ++# 12 SHA3-256 ++# 14 SHA3-512 + # + %_source_filedigest_algorithm 8 + %_binary_filedigest_algorithm 8 +diff --git a/rpmio/digest_libgcrypt.c b/rpmio/digest_libgcrypt.c +index 1e815416d..07103cf18 100644 +--- a/rpmio/digest_libgcrypt.c ++++ b/rpmio/digest_libgcrypt.c +@@ -46,6 +46,10 @@ size_t rpmDigestLength(int hashalgo) + return 48; + case RPM_HASH_SHA512: + return 64; ++ case RPM_HASH_SHA3_256: ++ return 32; ++ case RPM_HASH_SHA3_512: ++ return 64; + default: + return 0; + } +@@ -66,6 +70,10 @@ static int hashalgo2gcryalgo(int hashalgo) + return GCRY_MD_SHA384; + case RPM_HASH_SHA512: + return GCRY_MD_SHA512; ++ case RPM_HASH_SHA3_256: ++ return GCRY_MD_SHA3_256; ++ case RPM_HASH_SHA3_512: ++ return GCRY_MD_SHA3_512; + default: + return 0; + } +diff --git a/rpmio/digest_openssl.c b/rpmio/digest_openssl.c +index f8b12df93..8867441d0 100644 +--- a/rpmio/digest_openssl.c ++++ b/rpmio/digest_openssl.c +@@ -72,6 +72,12 @@ static const EVP_MD *getEVPMD(int hashalgo) + case RPM_HASH_SHA224: + return EVP_sha224(); + ++ case RPM_HASH_SHA3_256: ++ return EVP_sha3_256(); ++ ++ case RPM_HASH_SHA3_512: ++ return EVP_sha3_512(); ++ + default: + return EVP_md_null(); + } +-- +2.50.1 + + +From 3b172765d35ebab9333665daade71aba72a267d2 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 11 Mar 2025 17:16:19 +0200 +Subject: [PATCH 2/8] Fix OpenPGP packet (tag 17) name & description + +PGPTAG_PHOTOID is some early PGP-era legacy name for it, RFC-4880 +already calls it "User attribute packet". + +(cherry picked from commit e07341097f0b8361712fe3aafb59ac1f3e7af25d) +--- + include/rpm/rpmpgp.h | 4 +++- + rpmio/rpmpgpval.h | 2 +- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/include/rpm/rpmpgp.h b/include/rpm/rpmpgp.h +index 1f356f412..6d07d0a17 100644 +--- a/include/rpm/rpmpgp.h ++++ b/include/rpm/rpmpgp.h +@@ -62,7 +62,7 @@ typedef enum pgpTag_e { + PGPTAG_USER_ID = 13, /*!< User ID */ + PGPTAG_PUBLIC_SUBKEY = 14, /*!< Public Subkey */ + PGPTAG_COMMENT_OLD = 16, /*!< Comment (from OpenPGP draft) */ +- PGPTAG_PHOTOID = 17, /*!< PGP's photo ID */ ++ PGPTAG_USER_ATTRIBUTE = 17, /*!< User Attribute packet */ + PGPTAG_ENCRYPTED_MDC = 18, /*!< Integrity protected encrypted data */ + PGPTAG_MDC = 19, /*!< Manipulaion detection code packet */ + PGPTAG_PRIVATE_60 = 60, /*!< Private or Experimental Values */ +@@ -71,6 +71,8 @@ typedef enum pgpTag_e { + PGPTAG_CONTROL = 63 /*!< Control (GPG) */ + } pgpTag; + ++#define PGPTAG_PHOTOID PGPTAG_USER_ATTRIBUTE /* legacy name */ ++ + /** \ingroup rpmpgp + * 5.1. Public-Key Encrypted Session Key Packets (Tag 1) + * +diff --git a/rpmio/rpmpgpval.h b/rpmio/rpmpgpval.h +index ad8ed08e2..037db075e 100644 +--- a/rpmio/rpmpgpval.h ++++ b/rpmio/rpmpgpval.h +@@ -140,7 +140,7 @@ static struct pgpValTbl_s const pgpTagTbl[] = { + { PGPTAG_USER_ID, "User ID" }, + { PGPTAG_PUBLIC_SUBKEY, "Public Subkey" }, + { PGPTAG_COMMENT_OLD, "Comment (from OpenPGP draft)" }, +- { PGPTAG_PHOTOID, "PGP's photo ID" }, ++ { PGPTAG_USER_ATTRIBUTE, "User Attribute" }, + { PGPTAG_ENCRYPTED_MDC, "Integrity protected encrypted data" }, + { PGPTAG_MDC, "Manipulaion detection code packet" }, + { PGPTAG_PRIVATE_60, "Private #60" }, +-- +2.50.1 + + +From e2bd1fbdaee9533ccee20a901fa11adf21155e82 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 11 Mar 2025 14:57:57 +0200 +Subject: [PATCH 3/8] Add new tag and algorithm ID's and descriptions from + RFC-9580 + +While most of the details are handled by rpm-sequoia, rpm needs to know +at least the algorithm id's. Add the other tags while at it, it's not +much anyhow. + +Also add the SHA3 description texts that were missing from +9344367edf654fb41a136793b68cea9abb892fb9 + +There's no way to test this stuff at this point though, we need to +wait until sequoia-pgp 2.0 and a compatible rpm-sequoia. OTOH some +implementations might need these symbols in place. + +Fixes: #3631 +(backported from commit c2f760188421351f5ac38ec6e18c3eb4daf2e750) +--- + include/rpm/rpmpgp.h | 15 +++++++++++++-- + rpmio/rpmpgpval.h | 13 +++++++++++++ + 2 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/include/rpm/rpmpgp.h b/include/rpm/rpmpgp.h +index 6d07d0a17..92f10e11d 100644 +--- a/include/rpm/rpmpgp.h ++++ b/include/rpm/rpmpgp.h +@@ -65,6 +65,7 @@ typedef enum pgpTag_e { + PGPTAG_USER_ATTRIBUTE = 17, /*!< User Attribute packet */ + PGPTAG_ENCRYPTED_MDC = 18, /*!< Integrity protected encrypted data */ + PGPTAG_MDC = 19, /*!< Manipulaion detection code packet */ ++ PGPTAG_PADDING = 21, /*!< Padding packet */ + PGPTAG_PRIVATE_60 = 60, /*!< Private or Experimental Values */ + PGPTAG_COMMENT = 61, /*!< Comment */ + PGPTAG_PRIVATE_62 = 62, /*!< Private or Experimental Values */ +@@ -137,7 +138,8 @@ typedef enum pgpSigType_e { + PGPSIGTYPE_KEY_REVOKE = 0x20, /*!< Key revocation */ + PGPSIGTYPE_SUBKEY_REVOKE = 0x28, /*!< Subkey revocation */ + PGPSIGTYPE_CERT_REVOKE = 0x30, /*!< Certification revocation */ +- PGPSIGTYPE_TIMESTAMP = 0x40 /*!< Timestamp */ ++ PGPSIGTYPE_TIMESTAMP = 0x40, /*!< Timestamp */ ++ PGPSIGTYPE_THIRD_PARTY = 0x50, /*!< Third-Party Confirmation */ + } pgpSigType; + + /** \ingroup rpmpgp +@@ -174,7 +176,11 @@ typedef enum pgpPubkeyAlgo_e { + PGPPUBKEYALGO_ECDSA = 19, /*!< ECDSA */ + PGPPUBKEYALGO_ELGAMAL = 20, /*!< Elgamal */ + PGPPUBKEYALGO_DH = 21, /*!< Diffie-Hellman (X9.42) */ +- PGPPUBKEYALGO_EDDSA = 22 /*!< EdDSA */ ++ PGPPUBKEYALGO_EDDSA = 22, /*!< EdDSA */ ++ PGPPUBKEYALGO_X25519 = 25, /*!< X25519 */ ++ PGPPUBKEYALGO_X448 = 26, /*!< X448 */ ++ PGPPUBKEYALGO_ED25519 = 27, /*!< Ed25519 */ ++ PGPPUBKEYALGO_ED448 = 28, /*!< Ed448 */ + } pgpPubkeyAlgo; + + /** \ingroup rpmpgp +@@ -213,6 +219,9 @@ typedef enum pgpSymkeyAlgo_e { + PGPSYMKEYALGO_AES_192 = 8, /*!< AES(192-bit key) */ + PGPSYMKEYALGO_AES_256 = 9, /*!< AES(256-bit key) */ + PGPSYMKEYALGO_TWOFISH = 10, /*!< TWOFISH(256-bit key) */ ++ PGPSYMKEYALGO_CAMELLIA_128 = 11, /*!< Camellia with 128-bit */ ++ PGPSYMKEYALGO_CAMELLIA_192 = 12, /*!< Camellia with 192-bit */ ++ PGPSYMKEYALGO_CAMELLIA_256 = 13, /*!< Camellia with 256-bit */ + PGPSYMKEYALGO_NOENCRYPT = 110 /*!< no encryption */ + } pgpSymkeyAlgo; + +@@ -445,6 +454,8 @@ typedef enum pgpSubType_e { + PGPSUBTYPE_REVOKE_REASON = 29, /*!< reason for revocation */ + PGPSUBTYPE_FEATURES = 30, /*!< feature flags (gpg) */ + PGPSUBTYPE_EMBEDDED_SIG = 32, /*!< embedded signature (gpg) */ ++ PGPSUBTYPE_INTREC_FINGERPRINT= 35, /*!< intended recipient fingerprint */ ++ PGPSUBTYPE_PFERER_AEAD = 39, /*!< preferred AEAD ciphercuites */ + + PGPSUBTYPE_INTERNAL_100 = 100, /*!< internal or user-defined */ + PGPSUBTYPE_INTERNAL_101 = 101, /*!< internal or user-defined */ +diff --git a/rpmio/rpmpgpval.h b/rpmio/rpmpgpval.h +index 037db075e..2b139c2f3 100644 +--- a/rpmio/rpmpgpval.h ++++ b/rpmio/rpmpgpval.h +@@ -25,6 +25,7 @@ static struct pgpValTbl_s const pgpSigTypeTbl[] = { + { PGPSIGTYPE_SUBKEY_REVOKE, "Subkey revocation signature" }, + { PGPSIGTYPE_CERT_REVOKE, "Certification revocation signature" }, + { PGPSIGTYPE_TIMESTAMP, "Timestamp signature" }, ++ { PGPSIGTYPE_THIRD_PARTY, "Third-Party Confirmation signature" }, + { -1, "Unknown signature type" }, + }; + +@@ -39,6 +40,10 @@ static struct pgpValTbl_s const pgpPubkeyTbl[] = { + { PGPPUBKEYALGO_ELGAMAL, "Elgamal" }, + { PGPPUBKEYALGO_DH, "Diffie-Hellman (X9.42)" }, + { PGPPUBKEYALGO_EDDSA, "EdDSA" }, ++ { PGPPUBKEYALGO_X25519, "X25519" }, ++ { PGPPUBKEYALGO_X448, "X448" }, ++ { PGPPUBKEYALGO_ED25519, "Ed25519" }, ++ { PGPPUBKEYALGO_ED448, "Ed448" }, + { -1, "Unknown public key algorithm" }, + }; + +@@ -54,6 +59,9 @@ static struct pgpValTbl_s const pgpSymkeyTbl[] = { + { PGPSYMKEYALGO_AES_192, "AES(192-bit key)" }, + { PGPSYMKEYALGO_AES_256, "AES(256-bit key)" }, + { PGPSYMKEYALGO_TWOFISH, "TWOFISH(256-bit key)" }, ++ { PGPSYMKEYALGO_CAMELLIA_128,"Camellia(128-bit key)" }, ++ { PGPSYMKEYALGO_CAMELLIA_192,"Camellia(192-bit key)" }, ++ { PGPSYMKEYALGO_CAMELLIA_256,"Camellia(256-bit key)" }, + { PGPSYMKEYALGO_NOENCRYPT, "no encryption" }, + { -1, "Unknown symmetric key algorithm" }, + }; +@@ -77,6 +85,8 @@ static struct pgpValTbl_s const pgpHashTbl[] = { + { PGPHASHALGO_SHA384, "SHA384" }, + { PGPHASHALGO_SHA512, "SHA512" }, + { PGPHASHALGO_SHA224, "SHA224" }, ++ { PGPHASHALGO_SHA3_256, "SHA3-256" }, ++ { PGPHASHALGO_SHA3_512, "SHA3-512" }, + { -1, "Unknown hash algorithm" }, + }; + +@@ -109,6 +119,8 @@ static struct pgpValTbl_s const pgpSubTypeTbl[] = { + { PGPSUBTYPE_REVOKE_REASON, "reason for revocation" }, + { PGPSUBTYPE_FEATURES, "features" }, + { PGPSUBTYPE_EMBEDDED_SIG, "embedded signature" }, ++ { PGPSUBTYPE_INTREC_FINGERPRINT,"intended recipient fingerprint" }, ++ { PGPSUBTYPE_PFERER_AEAD, "preferred AEAD ciphersuites" }, + + { PGPSUBTYPE_INTERNAL_100, "internal subpkt type 100" }, + { PGPSUBTYPE_INTERNAL_101, "internal subpkt type 101" }, +@@ -143,6 +155,7 @@ static struct pgpValTbl_s const pgpTagTbl[] = { + { PGPTAG_USER_ATTRIBUTE, "User Attribute" }, + { PGPTAG_ENCRYPTED_MDC, "Integrity protected encrypted data" }, + { PGPTAG_MDC, "Manipulaion detection code packet" }, ++ { PGPTAG_PADDING, "Padding" }, + { PGPTAG_PRIVATE_60, "Private #60" }, + { PGPTAG_COMMENT, "Comment" }, + { PGPTAG_PRIVATE_62, "Private #62" }, +-- +2.50.1 + + +From 31fbcaebaac0ad4e3b863813833156d4e4d4b470 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 25 Jun 2025 13:32:49 +0300 +Subject: [PATCH 4/8] Add algorithm ID's for PQC public key algorithms from the + current draft + +PQC in OpenPGP is still just a draft [*], but these have been assigned and +unchanged since November 2024. If they change, the worst that can happen is +we show an incorrect string as the algorithm identification, rpm doesn't +use them in any other way. In the meanwhile, this lets us get meaningful +output when testing PQC stuff. + +[*] https://github.com/openpgp-pqc/draft-openpgp-pqc/blob/main/draft-ietf-openpgp-pqc.md + +Fixes: #3632 +(cherry picked from commit 2659fb1fbcea28b703116e7de6a211729869cba6) +--- + include/rpm/rpmpgp.h | 7 +++++++ + rpmio/rpmpgpval.h | 7 +++++++ + 2 files changed, 14 insertions(+) + +diff --git a/include/rpm/rpmpgp.h b/include/rpm/rpmpgp.h +index 92f10e11d..052fdb54a 100644 +--- a/include/rpm/rpmpgp.h ++++ b/include/rpm/rpmpgp.h +@@ -181,6 +181,13 @@ typedef enum pgpPubkeyAlgo_e { + PGPPUBKEYALGO_X448 = 26, /*!< X448 */ + PGPPUBKEYALGO_ED25519 = 27, /*!< Ed25519 */ + PGPPUBKEYALGO_ED448 = 28, /*!< Ed448 */ ++ PGPPUBKEYALGO_ML_DSA65_ED25519 = 30, /*!< ML-DSA-65+Ed25519 */ ++ PGPPUBKEYALGO_ML_DSA87_ED448 = 31, /*!< ML-DSA-87+Ed448 */ ++ PGPPUBKEYALGO_SLH_DSA_SHAKE_128S = 32, /*!< SLH-DSA-SHAKE-128s */ ++ PGPPUBKEYALGO_SLH_DSA_SHAKE_128F = 33, /*!< SLH-DSA-SHAKE-128f */ ++ PGPPUBKEYALGO_SLH_DSA_SHAKE_256S = 34, /*!< SLH-DSA-SHAKE-256s */ ++ PGPPUBKEYALGO_ML_KEM768_X25519 = 35, /*!< ML-KEM-768+X25519 */ ++ PGPPUBKEYALGO_ML_KEM1024_X448 = 36, /*!< ML-KEM-1024+X448 */ + } pgpPubkeyAlgo; + + /** \ingroup rpmpgp +diff --git a/rpmio/rpmpgpval.h b/rpmio/rpmpgpval.h +index 2b139c2f3..1f9429851 100644 +--- a/rpmio/rpmpgpval.h ++++ b/rpmio/rpmpgpval.h +@@ -44,6 +44,13 @@ static struct pgpValTbl_s const pgpPubkeyTbl[] = { + { PGPPUBKEYALGO_X448, "X448" }, + { PGPPUBKEYALGO_ED25519, "Ed25519" }, + { PGPPUBKEYALGO_ED448, "Ed448" }, ++ { PGPPUBKEYALGO_ML_DSA65_ED25519, "ML-DSA-65+Ed25519" }, ++ { PGPPUBKEYALGO_ML_DSA87_ED448, "ML-DSA-87+Ed448" }, ++ { PGPPUBKEYALGO_SLH_DSA_SHAKE_128S, "SLH-DSA-SHAKE-128s" }, ++ { PGPPUBKEYALGO_SLH_DSA_SHAKE_128F, "SLH-DSA-SHAKE-128f" }, ++ { PGPPUBKEYALGO_SLH_DSA_SHAKE_256S, "SLH-DSA-SHAKE-256s" }, ++ { PGPPUBKEYALGO_ML_KEM768_X25519, "ML-KEM-768+X25519" }, ++ { PGPPUBKEYALGO_ML_KEM1024_X448, "ML-KEM-1024+X448" }, + { -1, "Unknown public key algorithm" }, + }; + +-- +2.50.1 + + +From c4d7e3f95800af8a8765df542804b8481b540253 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 1 Jul 2025 11:48:12 +0300 +Subject: [PATCH 5/8] Support updating individual IDs in a digest bundle + +Up to now, rpm digest bundles have only needed to support data from a +single stream - just covering different ranges and algorithms. But +OpenPGP v6 signature salt is a random per-signature thing that we need +to feed into the digest before the actual data, so we need to be able to +update each ID in a bundle individually too. + +Luckily this is easy to do. Add a small test-program to exercise it, +we can't yet actually use it for testing a real-world V6 scenario +anyway. + +Fixes: #3845 +(backported from commit 28e4f05c71202b2614619e9d9a51af9443014f34) +--- + include/rpm/rpmcrypto.h | 11 +++++++++++ + rpmio/digest.c | 13 +++++++++++++ + 2 files changed, 24 insertions(+) + +diff --git a/include/rpm/rpmcrypto.h b/include/rpm/rpmcrypto.h +index d8e31a222..e9ab646fc 100644 +--- a/include/rpm/rpmcrypto.h ++++ b/include/rpm/rpmcrypto.h +@@ -144,6 +144,17 @@ int rpmDigestBundleAddID(rpmDigestBundle bundle, int algo, int id, + */ + int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len); + ++/** \ingroup rpmcrypto ++ * Update context of an individual ID within bundle with next plain text buffer. ++ * @param bundle digest bundle ++ * @param id id of digest (arbitrary, must be > 0) ++ * @param data next data buffer ++ * @param len no. bytes of data ++ * @return 0 on success ++ */ ++int rpmDigestBundleUpdateID(rpmDigestBundle bundle, int id, ++ const void *data, size_t len); ++ + /** \ingroup rpmcrypto + * Return digest from a bundle and destroy context, see rpmDigestFinal(). + * +diff --git a/rpmio/digest.c b/rpmio/digest.c +index e60d171d7..ed1f318fa 100644 +--- a/rpmio/digest.c ++++ b/rpmio/digest.c +@@ -91,6 +91,19 @@ int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len) + return rc; + } + ++int rpmDigestBundleUpdateID(rpmDigestBundle bundle, int id, ++ const void *data, size_t len) ++{ ++ int rc = -1; ++ int ix = -1; ++ ++ if (bundle && data && len > 0 && id > 0) { ++ ix = findID(bundle, id); ++ if (ix >= 0) ++ rc = rpmDigestUpdate(bundle->digests[ix], data, len); ++ } ++ return rc; ++} + int rpmDigestBundleFinal(rpmDigestBundle bundle, int id, + void ** datap, size_t * lenp, int asAscii) + { +-- +2.50.1 + + +From db4704ea3b47f5e38ad1606a8c3cfe27059b084f Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 1 Jul 2025 12:15:03 +0300 +Subject: [PATCH 6/8] Support OpenPGP v6 signature pre-salting + +OpenPGP v6 signature salt is a random per-signature thing that we need +to feed into the digest before the actual data. For that we need +rpm-sequoia support, the first version to have it is 1.9.0. + +The tests for this are already written, just need to be adjusted a bit +and uncommented, so do that. Kudos to Jakub for covering this part via +PR #3844 (squashed into this commit)! + +Co-authored-by: Jakub Jelen + +Fixes: #3846 +(backported from commit c36c717f41683953b9c23e447a8df0d0ac7c845c) +--- + INSTALL | 2 +- + include/rpm/rpmpgp.h | 2 ++ + lib/rpmvs.c | 14 ++++++++++++-- + rpmio/CMakeLists.txt | 2 +- + rpmio/rpmpgp_sequoia.c | 3 +++ + 5 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/INSTALL b/INSTALL +index 0a86822b0..82c91d5b0 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -28,7 +28,7 @@ The source for the file utility + library is available from + + You will need a cryptographic library to support digests and + signatures. This depends on the OpenPGP parser used: the default is +-rpm-sequoia library (>= 1.3.0 required), which is available from ++rpm-sequoia library (>= 1.9.0 required), which is available from + https://github.com/rpm-software-management/rpm-sequoia + + Use of rpm-sequoia is strongly recommended. Most importantly, the internal +diff --git a/include/rpm/rpmpgp.h b/include/rpm/rpmpgp.h +index 052fdb54a..1076eecbd 100644 +--- a/include/rpm/rpmpgp.h ++++ b/include/rpm/rpmpgp.h +@@ -1205,6 +1205,8 @@ int pgpDigParamsVersion(pgpDigParams digp); + */ + uint32_t pgpDigParamsCreationTime(pgpDigParams digp); + ++int pgpDigParamsSalt(pgpDigParams digp, const uint8_t **datap, size_t *lenp); ++ + /** \ingroup rpmpgp + * Destroy parsed OpenPGP packet parameter(s). + * @param digp parameter container +diff --git a/lib/rpmvs.c b/lib/rpmvs.c +index e22051a92..3d6227330 100644 +--- a/lib/rpmvs.c ++++ b/lib/rpmvs.c +@@ -424,8 +424,18 @@ void rpmvsInitRange(struct rpmvs_s *sis, int range) + for (int i = 0; i < sis->nsigs; i++) { + struct rpmsinfo_s *sinfo = &sis->sigs[i]; + if (sinfo->range & range) { +- if (sinfo->rc == RPMRC_OK) +- rpmDigestBundleAddID(sis->bundle, sinfo->hashalgo, sinfo->id, 0); ++ if (sinfo->rc != RPMRC_OK) ++ continue; ++ ++ rpmDigestBundleAddID(sis->bundle, sinfo->hashalgo, sinfo->id, 0); ++ /* OpenPGP v6 signatures need a grain of salt to go */ ++ if (sinfo->sig) { ++ const uint8_t *salt = NULL; ++ size_t slen = 0; ++ if (pgpDigParamsSalt(sinfo->sig, &salt, &slen) == 0 && salt) { ++ rpmDigestBundleUpdateID(sis->bundle, sinfo->id, salt, slen); ++ } ++ } + } + } + } +diff --git a/rpmio/CMakeLists.txt b/rpmio/CMakeLists.txt +index aec48966c..1017b2e09 100644 +--- a/rpmio/CMakeLists.txt ++++ b/rpmio/CMakeLists.txt +@@ -15,7 +15,7 @@ target_include_directories(librpmio PRIVATE + + + if (WITH_SEQUOIA) +- pkg_check_modules(RPMSEQUOIA REQUIRED IMPORTED_TARGET rpm-sequoia>=1.8.0) ++ pkg_check_modules(RPMSEQUOIA REQUIRED IMPORTED_TARGET rpm-sequoia>=1.9.0) + target_sources(librpmio PRIVATE rpmpgp_sequoia.c) + target_link_libraries(librpmio PRIVATE PkgConfig::RPMSEQUOIA) + else() +diff --git a/rpmio/rpmpgp_sequoia.c b/rpmio/rpmpgp_sequoia.c +index d0b673953..07b7bbffe 100644 +--- a/rpmio/rpmpgp_sequoia.c ++++ b/rpmio/rpmpgp_sequoia.c +@@ -33,6 +33,9 @@ W(const uint8_t *, pgpDigParamsSignID, (pgpDigParams digp), (digp)) + W(const char *, pgpDigParamsUserID, (pgpDigParams digp), (digp)) + W(int, pgpDigParamsVersion, (pgpDigParams digp), (digp)) + W(uint32_t, pgpDigParamsCreationTime, (pgpDigParams digp), (digp)) ++W(int, pgpDigParamsSalt, ++ (pgpDigParams digp, const uint8_t **datap, size_t *lenp), ++ (digp, datap, lenp)) + W(rpmRC, pgpVerifySignature, + (pgpDigParams key, pgpDigParams sig, DIGEST_CTX hashctx), + (key, sig, hashctx)) +-- +2.50.1 + + +From c47dc0c4604a20ede996e93650ba9290c26d9909 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= +Date: Fri, 4 Apr 2025 16:59:41 +0200 +Subject: [PATCH 7/8] Return -1 from fdSize() for non-regular files + +I noticed that importing a key from a pipe did not work: + + $ cat /tmp/key | rpmkeys --import - + error: -: import read failed(0). + +While importing from a regular file on stdin worked: + + $ < /tmp/key rpmkeys --import - + +The cause was fdSize() returning 0 for a pipe descriptor. +rpmcliImportPubkeys() calls rpmioSlurp() which calls fdSize() like +this: + + size = fdSize(fd); + blen = (size >= 0 ? size : blenmax); + if (blen) { + /* read from the descriptor */ + } + +As a result, rpmioSlurp() concluded that the "file" was empty and +rpmcliImportPubkeys() reported an error: + + iorc = rpmioSlurp(fn, &buf, &blen); + if (iorc || buf == NULL || blen < 64) { + rpmlog(RPMLOG_ERR, _("%s: import read failed(%d).\n"), fn, iorc); + +This patch changes fdSize() to return an error for non-regular files. +This is in line with fstat(3) manual: + + st_size + This field gives the size of the file (if it is + a regular file or a symbolic link) in bytes. The size + of a symbolic link is the length of the pathname it + contains, without a terminating null byte. + +Returning an error on unsupported descriptors was pretty common before +commit 852398f8c6dcb4ad5ed0310e49e7d342a262be91 +("Lose unnecessary url type checking from fdSize()"). + +(cherry picked from commit 063427c5f72e1cb50a61cb0973be127a00e93c2c) +--- + rpmio/rpmio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rpmio/rpmio.c b/rpmio/rpmio.c +index 6069274a7..a8fdd00b9 100644 +--- a/rpmio/rpmio.c ++++ b/rpmio/rpmio.c +@@ -248,7 +248,7 @@ off_t fdSize(FD_t fd) + struct stat sb; + off_t rc = -1; + +- if (fd != NULL && fstat(Fileno(fd), &sb) == 0) ++ if (fd != NULL && fstat(Fileno(fd), &sb) == 0 && S_ISREG(sb.st_mode)) + rc = sb.st_size; + return rc; + } +-- +2.50.1 + + +From 70de5d8eb66925a59dafaf53fdef2a5dc8afa860 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= +Date: Fri, 4 Apr 2025 17:32:05 +0200 +Subject: [PATCH 8/8] Handle fdSize() failure in rpmSign() + +This mistake existed before "Return -1 from fdSize() for non-regular +files" commit. The computed offsets could reach negative values. + +(cherry picked from commit 0ecca60d56f163d591a97e9f216573d3c3d5ef7f) +--- + sign/rpmgensig.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/sign/rpmgensig.c b/sign/rpmgensig.c +index a96bf7ed0..4596b5116 100644 +--- a/sign/rpmgensig.c ++++ b/sign/rpmgensig.c +@@ -647,6 +647,7 @@ static int rpmSign(const char *rpm, int deleting, int flags) + int res = -1; /* assume failure */ + rpmRC rc; + struct rpmtd_s utd; ++ off_t fileSize; + off_t headerStart; + off_t sigStart; + struct sigTarget_s sigt_v3; +@@ -734,11 +735,16 @@ static int rpmSign(const char *rpm, int deleting, int flags) + } else if (deleting) { /* Nuke all the signature tags. */ + deleteSigs(sigh); + } else { ++ fileSize = fdSize(fd); ++ if (fileSize < 0) { ++ rpmlog(RPMLOG_ERR, _("Could not get a file size of %s\n"), rpm); ++ goto exit; ++ } + /* Signature target containing header + payload */ + sigt_v3.fd = fd; + sigt_v3.start = headerStart; + sigt_v3.fileName = rpm; +- sigt_v3.size = fdSize(fd) - headerStart; ++ sigt_v3.size = fileSize - headerStart; + + /* Signature target containing only header */ + sigt_v4 = sigt_v3; +-- +2.50.1 + diff --git a/rpm-4.19.x-rpmkeys-add-list-erase.patch b/rpm-4.19.x-rpmkeys-add-list-erase.patch new file mode 100644 index 0000000..a23a32e --- /dev/null +++ b/rpm-4.19.x-rpmkeys-add-list-erase.patch @@ -0,0 +1,466 @@ +From aa48e52daf219c224648528053ec41c358930f16 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Wed, 21 Feb 2024 08:25:27 +0100 +Subject: [PATCH 1/5] Add --list and --delete to rpmkeys + +This is a bit of a hack as it manipulates the parsed cli parameters to +to the "right thing" and then calls rpmcliQuery and rpmErase. + +(cherry picked from commit 1dc7e76fa51bca54f0eb75660ab6e68216289eb6) +--- + docs/man/rpmkeys.8.md | 18 +++++++++++++++--- + tools/rpmkeys.c | 40 ++++++++++++++++++++++++++++++++++------ + 2 files changed, 49 insertions(+), 9 deletions(-) + +diff --git a/docs/man/rpmkeys.8.md b/docs/man/rpmkeys.8.md +index 9ea0a2079..cbc619001 100644 +--- a/docs/man/rpmkeys.8.md ++++ b/docs/man/rpmkeys.8.md +@@ -12,15 +12,19 @@ rpmkeys - RPM Keyring + SYNOPSIS + ======== + +-**rpmkeys** {**\--import\|\--checksig**} ++**rpmkeys** {**\--list\|\--import\|\--delete\|\--checksig**} + + DESCRIPTION + =========== + + The general forms of rpm digital signature commands are + ++**rpmkeys** **\--list** \[*KEYHASH \...*\] ++ + **rpmkeys** **\--import** *PUBKEY \...* + ++**rpmkeys** **\--delete** *KEYHASH \...* ++ + **rpmkeys** {**-K\|\--checksig**} *PACKAGE\_FILE \...* + + The **\--checksig** option checks all the digests and signatures +@@ -37,13 +41,21 @@ example, all currently imported public keys can be displayed by: + + **rpm -qa gpg-pubkey\*** + +-Details about a specific public key, when imported, can be displayed by ++A more convenient way to display them is ++ ++**rpmkeys** **\--list** ++ ++More details about a specific public key, when imported, can be displayed by + querying. Here\'s information about the Red Hat GPG/DSA key: + + **rpm -qi gpg-pubkey-db42a60e** + + Finally, public keys can be erased after importing just like packages. +-Here\'s how to remove the Red Hat GPG/DSA key ++Here\'s how to remove the Red Hat GPG/DSA key: ++ ++**rpmkeys** **\--delete db42a60e** ++ ++Or alternatively: + + **rpm -e gpg-pubkey-db42a60e** + +diff --git a/tools/rpmkeys.c b/tools/rpmkeys.c +index afaffe501..adb65735c 100644 +--- a/tools/rpmkeys.c ++++ b/tools/rpmkeys.c +@@ -2,6 +2,7 @@ + + #include + #include ++#include + #include "cliutils.h" + #include "debug.h" + +@@ -22,12 +23,10 @@ static struct poptOption keyOptsTable[] = { + N_("import an armored public key"), NULL }, + { "test", '\0', POPT_ARG_NONE, &test, 0, + N_("don't import, but tell if it would work or not"), NULL }, +-#if 0 +- { "delete-key", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, ++ { "delete", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, ++ N_("delete keys from RPM keyring"), NULL }, ++ { "list", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_LISTKEY, + N_("list keys from RPM keyring"), NULL }, +- { "list-keys", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_LISTKEY, +- N_("list keys from RPM keyring"), NULL }, +-#endif + POPT_TABLEEND + }; + +@@ -42,6 +41,21 @@ static struct poptOption optionsTable[] = { + POPT_TABLEEND + }; + ++static ARGV_t gpgkeyargs(ARGV_const_t args) { ++ ARGV_t gpgargs = NULL; ++ for (char * const * arg = args; *arg; arg++) { ++ if (strncmp(*arg, "gpg-pubkey-", 11)) { ++ char * gpgarg = NULL; ++ rstrscat(&gpgarg, "gpg-pubkey-", *arg, NULL); ++ argvAdd(&gpgargs, gpgarg); ++ free(gpgarg); ++ } else { ++ argvAdd(&gpgargs, *arg); ++ } ++ } ++ return gpgargs; ++} ++ + int main(int argc, char *argv[]) + { + int ec = EXIT_FAILURE; +@@ -73,9 +87,23 @@ int main(int argc, char *argv[]) + rpmtsSetFlags(ts, (rpmtsFlags(ts)|RPMTRANS_FLAG_TEST)); + ec = rpmcliImportPubkeys(ts, args); + break; +- /* XXX TODO: actually implement these... */ + case MODE_DELKEY: ++ struct rpmInstallArguments_s * ia = &rpmIArgs; ++ ARGV_t gpgargs = gpgkeyargs(args); ++ ec = rpmErase(ts, ia, gpgargs); ++ argvFree(gpgargs); ++ break; + case MODE_LISTKEY: ++ ARGV_t query = NULL; ++ if (args != NULL) { ++ query = gpgkeyargs(args); ++ } else { ++ argvAdd(&query, "gpg-pubkey"); ++ } ++ QVA_t qva = &rpmQVKArgs; ++ rstrcat(&qva->qva_queryFormat, "%{version}-%{release}: %{summary}\n"); ++ ec = rpmcliQuery(ts, &rpmQVKArgs, (ARGV_const_t) query); ++ query = argvFree(query); + break; + default: + argerror(_("only one major mode may be specified")); +-- +2.50.1 + + +From a04cbdf459b75e326ad9a3e78cabd2980c5c410e Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 15 Oct 2024 09:28:36 +0300 +Subject: [PATCH 2/5] Having no keys imported is not an error + +...any more than "ls" in an empty directory is. + +Resolves: 3556 +Co-authored-by: Florian Festi + +(backported from commit d666883624c5f2905c0bc70112895c33605ca264) +--- + tools/rpmkeys.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tools/rpmkeys.c b/tools/rpmkeys.c +index adb65735c..d2c2644e2 100644 +--- a/tools/rpmkeys.c ++++ b/tools/rpmkeys.c +@@ -95,12 +95,13 @@ int main(int argc, char *argv[]) + break; + case MODE_LISTKEY: + ARGV_t query = NULL; ++ QVA_t qva = &rpmQVKArgs; + if (args != NULL) { + query = gpgkeyargs(args); + } else { ++ qva->qva_source |= RPMQV_ALL; + argvAdd(&query, "gpg-pubkey"); + } +- QVA_t qva = &rpmQVKArgs; + rstrcat(&qva->qva_queryFormat, "%{version}-%{release}: %{summary}\n"); + ec = rpmcliQuery(ts, &rpmQVKArgs, (ARGV_const_t) query); + query = argvFree(query); +-- +2.50.1 + + +From cd0cf8fbdde64656b851ad6056f0c40e18c90a46 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Fri, 15 Nov 2024 13:15:58 +0100 +Subject: [PATCH 3/5] Add short CLI commands to rpmkeys + +Switch to short versions in the test suite at a few places. + +Resolves: #3435 +(backported from commit 35cfb7de9842dbec35adcb9ad9a632df49f35102) +--- + docs/man/rpmkeys.8.md | 6 +++--- + tools/rpmkeys.c | 8 ++++---- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/docs/man/rpmkeys.8.md b/docs/man/rpmkeys.8.md +index cbc619001..77bce49a9 100644 +--- a/docs/man/rpmkeys.8.md ++++ b/docs/man/rpmkeys.8.md +@@ -19,11 +19,11 @@ DESCRIPTION + + The general forms of rpm digital signature commands are + +-**rpmkeys** **\--list** \[*KEYHASH \...*\] ++**rpmkeys** {**-l\|\--list**} \[*KEYHASH \...*\] + +-**rpmkeys** **\--import** *PUBKEY \...* ++**rpmkeys** {**-i\|\--import**} *PUBKEY \...* + +-**rpmkeys** **\--delete** *KEYHASH \...* ++**rpmkeys** {**-d\|\--delete**} *KEYHASH \...* + + **rpmkeys** {**-K\|\--checksig**} *PACKAGE\_FILE \...* + +diff --git a/tools/rpmkeys.c b/tools/rpmkeys.c +index d2c2644e2..79bb3e236 100644 +--- a/tools/rpmkeys.c ++++ b/tools/rpmkeys.c +@@ -19,13 +19,13 @@ static int test = 0; + static struct poptOption keyOptsTable[] = { + { "checksig", 'K', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_CHECKSIG, + N_("verify package signature(s)"), NULL }, +- { "import", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_IMPORTKEY, ++ { "import", 'i', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_IMPORTKEY, + N_("import an armored public key"), NULL }, +- { "test", '\0', POPT_ARG_NONE, &test, 0, ++ { "test", 't', POPT_ARG_NONE, &test, 0, + N_("don't import, but tell if it would work or not"), NULL }, +- { "delete", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, ++ { "delete", 'd', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, + N_("delete keys from RPM keyring"), NULL }, +- { "list", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_LISTKEY, ++ { "list", 'l', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_LISTKEY, + N_("list keys from RPM keyring"), NULL }, + POPT_TABLEEND + }; +-- +2.50.1 + + +From 8b344dfe14eda275e85f3acece93f98f5cab1e37 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Fri, 15 Nov 2024 14:03:36 +0100 +Subject: [PATCH 4/5] Rename rpmkeys --delete to rpmkeys --erase + +Keep in line with the command names in rpm itself. +Keep --delete functional for compatibility reasons but remove it from +the docs. + +Related: #3435 +(backported from commit abb6ab1c43a946eb36a36227e39918580390a8e4) +--- + docs/man/rpmkeys.8.md | 4 ++-- + tools/rpmkeys.c | 7 +++++-- + 2 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/docs/man/rpmkeys.8.md b/docs/man/rpmkeys.8.md +index 77bce49a9..393845e62 100644 +--- a/docs/man/rpmkeys.8.md ++++ b/docs/man/rpmkeys.8.md +@@ -12,7 +12,7 @@ rpmkeys - RPM Keyring + SYNOPSIS + ======== + +-**rpmkeys** {**\--list\|\--import\|\--delete\|\--checksig**} ++**rpmkeys** {**\--list\|\--import\|\--erase\|\--checksig**} + + DESCRIPTION + =========== +@@ -23,7 +23,7 @@ The general forms of rpm digital signature commands are + + **rpmkeys** {**-i\|\--import**} *PUBKEY \...* + +-**rpmkeys** {**-d\|\--delete**} *KEYHASH \...* ++**rpmkeys** {**-e\|\--erase**} *KEYHASH \...* + + **rpmkeys** {**-K\|\--checksig**} *PACKAGE\_FILE \...* + +diff --git a/tools/rpmkeys.c b/tools/rpmkeys.c +index 79bb3e236..77eaa703f 100644 +--- a/tools/rpmkeys.c ++++ b/tools/rpmkeys.c +@@ -23,8 +23,11 @@ static struct poptOption keyOptsTable[] = { + N_("import an armored public key"), NULL }, + { "test", 't', POPT_ARG_NONE, &test, 0, + N_("don't import, but tell if it would work or not"), NULL }, +- { "delete", 'd', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, +- N_("delete keys from RPM keyring"), NULL }, ++ { "delete", 'd', (POPT_ARG_VAL|POPT_ARGFLAG_OR|POPT_ARGFLAG_DOC_HIDDEN), ++ &mode, MODE_DELKEY, ++ N_("Erase keys from RPM keyring"), NULL }, ++ { "erase", 'e', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, ++ N_("Erase keys from RPM keyring"), NULL }, + { "list", 'l', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_LISTKEY, + N_("list keys from RPM keyring"), NULL }, + POPT_TABLEEND +-- +2.50.1 + + +From 2cd7d8d91d3c8bb7bf800bc7982ee2f5926d5c15 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Fri, 15 Nov 2024 19:22:07 +0100 +Subject: [PATCH 5/5] Don't delete delete + +(backported from commit 4c99a010b23fd3832025d9cd2a5481a81f5c3353) +--- + docs/man/rpmkeys.8.md | 4 ++-- + tools/rpmkeys.c | 3 +-- + 2 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/docs/man/rpmkeys.8.md b/docs/man/rpmkeys.8.md +index 393845e62..afa470b6f 100644 +--- a/docs/man/rpmkeys.8.md ++++ b/docs/man/rpmkeys.8.md +@@ -12,7 +12,7 @@ rpmkeys - RPM Keyring + SYNOPSIS + ======== + +-**rpmkeys** {**\--list\|\--import\|\--erase\|\--checksig**} ++**rpmkeys** {**\--list\|\--import\|\--erase\|\--delete\|\--checksig**} + + DESCRIPTION + =========== +@@ -23,7 +23,7 @@ The general forms of rpm digital signature commands are + + **rpmkeys** {**-i\|\--import**} *PUBKEY \...* + +-**rpmkeys** {**-e\|\--erase**} *KEYHASH \...* ++**rpmkeys** {**-e\|\--erase\|-d\|\--delete**} *KEYHASH \...* + + **rpmkeys** {**-K\|\--checksig**} *PACKAGE\_FILE \...* + +diff --git a/tools/rpmkeys.c b/tools/rpmkeys.c +index 77eaa703f..f425a8648 100644 +--- a/tools/rpmkeys.c ++++ b/tools/rpmkeys.c +@@ -23,8 +23,7 @@ static struct poptOption keyOptsTable[] = { + N_("import an armored public key"), NULL }, + { "test", 't', POPT_ARG_NONE, &test, 0, + N_("don't import, but tell if it would work or not"), NULL }, +- { "delete", 'd', (POPT_ARG_VAL|POPT_ARGFLAG_OR|POPT_ARGFLAG_DOC_HIDDEN), +- &mode, MODE_DELKEY, ++ { "delete", 'd', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, + N_("Erase keys from RPM keyring"), NULL }, + { "erase", 'e', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_DELKEY, + N_("Erase keys from RPM keyring"), NULL }, +-- +2.50.1 + +diff -up rpm-4.19.1.1/docs/man/rpmkeys.8.orig rpm-4.19.1.1/docs/man/rpmkeys.8 +--- rpm-4.19.1.1/docs/man/rpmkeys.8.orig 2025-07-24 16:37:03.969960189 +0200 ++++ rpm-4.19.1.1/docs/man/rpmkeys.8 2025-07-24 16:37:28.946868811 +0200 +@@ -1,79 +1,73 @@ +-.\" Automatically generated by Pandoc 3.1.3 ++.\" Automatically generated by Pandoc 3.1.11.1 + .\" +-.\" Define V font for inline verbatim, using C font in formats +-.\" that render this, and otherwise B font. +-.ie "\f[CB]x\f[]"x" \{\ +-. ftr V B +-. ftr VI BI +-. ftr VB B +-. ftr VBI BI +-.\} +-.el \{\ +-. ftr V CR +-. ftr VI CI +-. ftr VB CB +-. ftr VBI CBI +-.\} + .TH "RPMKEYS" "8" "29 October 2010" "" "" +-.hy + .SH NAME +-.PP +-rpmkeys - RPM Keyring ++rpmkeys \- RPM Keyring + .SH SYNOPSIS +-.PP +-\f[B]rpmkeys\f[R] {\f[B]--import|--checksig\f[R]} ++\f[B]rpmkeys\f[R] ++{\f[B]\-\-list|\-\-import|\-\-erase|\-\-delete|\-\-checksig\f[R]} + .SH DESCRIPTION +-.PP + The general forms of rpm digital signature commands are + .PP +-\f[B]rpmkeys\f[R] \f[B]--import\f[R] \f[I]PUBKEY ...\f[R] ++\f[B]rpmkeys\f[R] {\f[B]\-l|\-\-list\f[R]} [\f[I]KEYHASH ...\f[R]] ++.PP ++\f[B]rpmkeys\f[R] {\f[B]\-i|\-\-import\f[R]} \f[I]PUBKEY ...\f[R] ++.PP ++\f[B]rpmkeys\f[R] {\f[B]\-e|\-\-erase|\-d|\-\-delete\f[R]} \f[I]KEYHASH ++\&...\f[R] + .PP +-\f[B]rpmkeys\f[R] {\f[B]-K|--checksig\f[R]} \f[I]PACKAGE_FILE ...\f[R] ++\f[B]rpmkeys\f[R] {\f[B]\-K|\-\-checksig\f[R]} \f[I]PACKAGE_FILE ++\&...\f[R] + .PP +-The \f[B]--checksig\f[R] option checks all the digests and signatures ++The \f[B]\-\-checksig\f[R] option checks all the digests and signatures + contained in \f[I]PACKAGE_FILE\f[R] to ensure the integrity and origin + of the package. + Note that signatures are now verified whenever a package is read, and +-\f[B]--checksig\f[R] is useful to verify all of the digests and ++\f[B]\-\-checksig\f[R] is useful to verify all of the digests and + signatures associated with a package. + .PP + Digital signatures cannot be verified without a public key. + An ASCII armored public key can be added to the \f[B]rpm\f[R] database +-using \f[B]--import\f[R]. ++using \f[B]\-\-import\f[R]. + An imported public key is carried in a header, and key ring management + is performed exactly like package management. + For example, all currently imported public keys can be displayed by: + .PP +-\f[B]rpm -qa gpg-pubkey*\f[R] ++\f[B]rpm \-qa gpg\-pubkey*\f[R] + .PP +-Details about a specific public key, when imported, can be displayed by +-querying. ++A more convenient way to display them is ++.PP ++\f[B]rpmkeys\f[R] \f[B]\-\-list\f[R] ++.PP ++More details about a specific public key, when imported, can be ++displayed by querying. + Here\[aq]s information about the Red Hat GPG/DSA key: + .PP +-\f[B]rpm -qi gpg-pubkey-db42a60e\f[R] ++\f[B]rpm \-qi gpg\-pubkey\-db42a60e\f[R] + .PP + Finally, public keys can be erased after importing just like packages. +-Here\[aq]s how to remove the Red Hat GPG/DSA key ++Here\[aq]s how to remove the Red Hat GPG/DSA key: + .PP +-\f[B]rpm -e gpg-pubkey-db42a60e\f[R] +-.SH SEE ALSO ++\f[B]rpmkeys\f[R] \f[B]\-\-delete db42a60e\f[R] + .PP ++Or alternatively: ++.PP ++\f[B]rpm \-e gpg\-pubkey\-db42a60e\f[R] ++.SH SEE ALSO + \f[B]popt\f[R](3), \f[B]rpm\f[R](8), \f[B]rpmdb\f[R](8), + \f[B]rpmsign\f[R](8), \f[B]rpm2cpio\f[R](8), \f[B]rpmbuild\f[R](8), + \f[B]rpmspec\f[R](8) + .PP +-\f[B]rpmkeys --help\f[R] - as rpm supports customizing the options via +-popt aliases it\[aq]s impossible to guarantee that what\[aq]s described +-in the manual matches what\[aq]s available. ++\f[B]rpmkeys \-\-help\f[R] \- as rpm supports customizing the options ++via popt aliases it\[aq]s impossible to guarantee that what\[aq]s ++described in the manual matches what\[aq]s available. + .PP + \f[B]http://www.rpm.org/ \f[R] + .SH AUTHORS + .IP +-.nf +-\f[C] ++.EX + Marc Ewing + Jeff Johnson + Erik Troan + Panu Matilainen +-\f[R] +-.fi ++.EE diff --git a/rpm.spec b/rpm.spec index 6ca2ea3..5d051a4 100644 --- a/rpm.spec +++ b/rpm.spec @@ -97,7 +97,7 @@ BuildRequires: doxygen %if %{with sequoia} %global crypto sequoia -BuildRequires: rpm-sequoia-devel >= 1.4.0 +BuildRequires: rpm-sequoia-devel >= 1.9.0 %else %global crypto openssl BuildRequires: openssl-devel @@ -162,6 +162,12 @@ rpm-4.18.90-weak-user-group.patch 0001-Ensure-binary-and-source-headers-are-identified-as-s.patch 0002-Add-support-for-spec-local-file-attributes-and-gener.patch +rpm-4.19.x-rpmkeys-add-list-erase.patch + +# PQC readiness +rpm-4.19.x-multisig.patch +rpm-4.19.x-pqc-algo.patch + # These are not yet upstream rpm-4.7.1-geode-i686.patch @@ -178,7 +184,7 @@ License: GPL-2.0-or-later OR LGPL-2.1-or-later Requires(meta): %{name} = %{version}-%{release} %if %{with sequoia} # >= 1.4.0 required for pgpVerifySignature2() and pgpPrtParams2() -Requires: rpm-sequoia%{_isa} >= 1.4.0 +Requires: rpm-sequoia%{_isa} >= 1.9.0 # Most systems should have a central package operations log Recommends: rpm-plugin-audit %endif @@ -391,6 +397,7 @@ cmake \ %{?with_libimaevm:-DWITH_IMAEVM=ON} \ %{!?with_libarchive:-DWITH_ARCHIVE=OFF} \ %{!?with_check:-DENABLE_TESTSUITE=OFF} \ + %{?with_sequoia:-DWITH_SEQUOIA=ON} \ %{!?with_sequoia:-DWITH_INTERNAL_OPENPGP=ON} \ %{!?with_sequoia:-DWITH_OPENSSL=ON } \ -DRPM_VENDOR=redhat \ @@ -450,6 +457,8 @@ rm $RPM_BUILD_ROOT/%{_defaultdocdir}/rpm/README.md # Signing macros for Sequoia install -m 644 %{SOURCE30} $RPM_BUILD_ROOT/%{_defaultdocdir}/rpm/ +rm $RPM_BUILD_ROOT/%{rpmhome}/rpmdump + %pre # Symlink all rpmdb files to the new location if we're still using /var/lib/rpm if [ -d /var/lib/rpm ]; then @@ -649,6 +658,10 @@ fi %changelog * Thu Jul 24 2025 Michal Domonkos - 4.19.1.1-18 +- Add support for multiple OpenPGP signatures per package (RHEL-100571) +- Add support for OpenPGP v6 signature pre-salting (RHEL-100571) +- Add support for PQC algorithms from RFC-9580 (RHEL-100571) +- Add --list and --erase commands to rpmkeys(8) (RHEL-105421) - Fix regression on dynamic subpackage RPMTAG_SOURCERPM missing (RHEL-102023) * Wed Jun 11 2025 Michal Domonkos - 4.19.1.1-17