Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
383eb0f
Add macros.rpmsign-sequoia to doc dir
ffesti Apr 9, 2025
ffc0473
Add sequoia-sq to test suite
ffesti Apr 9, 2025
c02b343
Drop unused header arguments, update comments in signing internals
pmatilai Nov 6, 2024
d070a2d
Refactor signature insertion to a helper function
pmatilai Nov 6, 2024
c4a8abf
Simplify the v3 signature logic a bit
pmatilai Nov 6, 2024
cb1d8f7
Add a new unique v6-only reserved signature tag below header base
pmatilai Feb 20, 2024
1ee45b8
Add tag extension for rpm format version detection
pmatilai Nov 20, 2024
dc420cf
Add support for multiple OpenPGP signatures per package, part 1/2
pmatilai Oct 30, 2024
5adbed2
Add support for multiple OpenPGP signatures per package, part 2/2
pmatilai Nov 18, 2024
5bc5f60
Adjust signature messages in tests
ffesti Apr 9, 2025
ccc004a
Adjust tests fully
ffesti Apr 4, 2025
230a544
Use RPMTAG_RPMFORMAT for rpm format detection in the signing code
pmatilai Nov 21, 2024
0d2e5e7
Bump rpm-sequoia requirement to 1.8 for OpenPGP v6 support
pmatilai May 14, 2025
f173404
Add OpenPGP v6 test keys
pmatilai May 14, 2025
15af386
Backport tests for v6-ed25519 keys
ffesti May 25, 2025
5cb1585
Fix v6 signatures on v6 packages tests
ffesti May 26, 2025
9f33b79
Fix some cases issues in rpmdump signature tag names
pmatilai May 26, 2025
d56980e
Teach rpmdump about RPMSIGTAG_OPENPGP
pmatilai May 26, 2025
465b5db
Add some signature tag level tests around signing
pmatilai May 26, 2025
15d9bb5
Fix v6 test cases
ffesti May 26, 2025
021957b
Fix rpm v6 signing with algorithms not supported by rpm v4 signatures
pmatilai May 15, 2025
0e9c2f9
Fix test case
ffesti Jun 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ install(FILES
manual/tags.md
manual/triggers.md
manual/tsort.md
macros.rpmsign-sequoia
TYPE DOC
)
26 changes: 26 additions & 0 deletions docs/macros.rpmsign-sequoia
Original file line number Diff line number Diff line change
@@ -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}}}

50 changes: 40 additions & 10 deletions docs/man/rpmsign.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 \...*

Expand All @@ -52,14 +55,41 @@ 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.

**\--fskpath** *KEY*
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*.

Expand Down
3 changes: 3 additions & 0 deletions docs/manual/tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -444,13 +445,15 @@ 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.
Obsoletenevrs | 5043 | string array | Formatted `name [op version]` obsolete dependency strings.
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. |
Expand Down
3 changes: 3 additions & 0 deletions include/rpm/rpmsign.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 6 additions & 0 deletions include/rpm/rpmtag.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -67,6 +68,8 @@ 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 */
#define RPMTAG_N RPMTAG_NAME /* s */
Expand Down Expand Up @@ -388,6 +391,7 @@ typedef enum rpmTag_e {
RPMTAG_SYSUSERS = 5109, /* s[] extension */
RPMTAG_BUILDSYSTEM = 5110, /* internal */
RPMTAG_BUILDOPTION = 5111, /* internal */
RPMTAG_RPMFORMAT = 5114, /* i */

RPMTAG_FIRSTFREE_TAG /*!< internal */
} rpmTag;
Expand Down Expand Up @@ -447,6 +451,8 @@ 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;


Expand Down
3 changes: 3 additions & 0 deletions include/rpm/rpmts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -125,13 +126,15 @@ typedef rpmFlags rpmVSFlags;
#define RPMVSF_MASK_NOSIGNATURES \
( RPMVSF_NODSAHEADER | \
RPMVSF_NORSAHEADER | \
RPMVSF_NOOPENPGP | \
RPMVSF_NODSA | \
RPMVSF_NORSA )
#define _RPMVSF_NOSIGNATURES RPMVSF_MASK_NOSIGNATURES

#define RPMVSF_MASK_NOHEADER \
( RPMVSF_NOSHA1HEADER | \
RPMVSF_NOSHA256HEADER | \
RPMVSF_NOOPENPGP | \
RPMVSF_NODSAHEADER | \
RPMVSF_NORSAHEADER )
#define _RPMVSF_NOHEADER RPMVSF_MASK_NOHEADER
Expand Down
24 changes: 21 additions & 3 deletions lib/formats.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,12 +413,12 @@ static char *jsonFormat(rpmtd td, char **emsg)
}

/* 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((uint8_t*)td->data, td->count, PGPTAG_SIGNATURE, &sigp)) {
if (pgpPrtParams(pkt, pktlen, PGPTAG_SIGNATURE, &sigp)) {
*emsg = xstrdup(_("(not an OpenPGP signature)"));
} else {
char dbuf[BUFSIZ];
Expand All @@ -445,6 +445,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)
{
Expand Down Expand Up @@ -575,7 +593,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",
Expand Down
1 change: 1 addition & 0 deletions lib/package.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
};

Expand Down
46 changes: 38 additions & 8 deletions lib/rpmvs.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "system.h"

#include <pthread.h>
#include <rpm/rpmbase64.h>
#include <rpm/rpmkeyring.h>
#include <rpm/rpmmacro.h>
#include <rpm/rpmlog.h>
Expand All @@ -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, },
Expand All @@ -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, }, },
Expand Down Expand Up @@ -132,14 +137,16 @@ static int validHex(const char *str, size_t slen)
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)
{
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);
Expand Down Expand Up @@ -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((const uint8_t *)data, dlen, PGPTAG_SIGNATURE,
&sinfo->sig, &lints);
Expand Down Expand Up @@ -235,8 +252,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)
Expand Down Expand Up @@ -290,11 +309,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;
}
Expand Down Expand Up @@ -330,7 +354,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 {
Expand Down
Loading