RPM Structure Overview

The RPM package format, as used by RedHat Linux, CentOS and others provides multiple mechanisms for verifying package integrity and authenticity before installation. Mechanisms for integrity and authenticity include:

  1. MD5 and SHA1 hashes of the rpm header (and optionally payload); and
  2. GPG signatures of the package.

In support of this, most distributions and third party repositories, use GPG signatures, as the MD5 and SHA1 hashes are added by default, and will be verified during installation. The GPG signatures on packages provide both authenticity (i.e. the packages are signed by the key’s owner), and integrity. The yum package manager can be configured to check GPG signatures during installation. Similarly it can also be configured to ignore or not validate these signatures during installation. The process of adding GPG signatures to packages, importing the GPG keys into yum / rpm, and configuring yum to validate the signatures is well documented, and well understood. Additionally, this is well supported by most versions of rpm / yum in common use.

Less understood, and less well documented is the process for changing the digest algorithm used for the hashing / signing of the rpms themselves. Unless otherwise specified, the digests used for (GPG) signing will be MD5 / SHA1.

Recently, in support of our ongoing NIAP evaluations for Titanium, we needed to switch to SHA256 for all digests (including those on our rpm packages). It should be noted that NIAP is not the only use case where SHA256 may be desirable, especially considering that both SHA1 and MD5 are vulnerable to hash collisions and are no longer recommended for general use.

Updating the gpg signatures on rpms to use SHA256 as the digest algorithm, is relatively straight forward although it is not well documented. The digest algorithm used for signing is controlled through the rpm macro, __gpg_sign_cmd.

The default value for this macro can be found by looking in the macro library, /usr/lib/rpm/macros (and searching for __gpg_sign_cmd). The macro itself relies on the gpg setting of digest-algo (which can be set on the command line, or through gpg’s configuration files – both user specific and system wide). The rpm macro itself can also be overridden to make it more explicit and very clearly identify what values are being used without needing to follow a web of dependencies and possible configuration values. The updated rpm macro for SHA256 digests is:

%__gpg_sign_cmd %{__gpg} gpg --force-v3-sigs --batch \ 
--verbose --no-armor --passphrase-fd 3 --no-secmem-warning \
-u "%{_gpg_name}" -sbo %{__signature_filename} \
--digest-algo sha256 %{__plaintext_filename}'

This can be set in either ~/.rpmmacros (to only impact the current user), or in /etc/rpm/macros (to force it system wide).

RPM Header digest format

We were also interested in changing the digests used in the headers of our generated rpms to use SHA256, instead of the legacy SHA1 / MD5. Changing the header digest turned out to be very poorly documented, and not well supported. The latest versions of Fedora Core use a more recent version of rpm, which supports header verification using SHA256 digests. On recent versions of Fedora, the rpmsign man page, and rpmsign usage commands make mention of using SHA256 digests.

Recent versions of the rpmsign man page discuss the use of the binary_filedigest_algorithm rpm macro. The man page goes on to describe the macro as: The supported algorithms are SHA1, SHA256, SHA384, and SHA512, which are represented as 2, 8, 9, and 10 respectively.

The man page doesn’t specifically spell it out, but there are rpm macros for both the rpm source and binary digests.

In order to use the digest macros, they also need to be set in either ~/.rpmmacros or /etc/rpm/macros, depending on whether you want the changes to impact only the current user or to be used system wide.

The macros (with the sha256 algorithms set) are:

%_source_filedigest_algorithm 8
%_binary_filedigest_algorithm 8

As it turns out, the version of rpm (4.11.3) that ships with the latest RHEL / CentOS doesn’t support SHA256 digests in the rpm headers. This version of rpm will completely ignore the macros during building, and will not display the digests if they exist (the new digest essentially changes the rpm header format).

As part of our testing, we built the latest rpm ( from source, and used it to build our rpms and perform some of the testing. The latest version of rpm supports the new digest flags / macros, with the caveat being these will be ignore by current versions of RHEL / CentOS.

It should be noted that the rpm –checksig command ignores a lot of details and really assumes a legacy format (based on the RPM header format). In order to get full details for the signatures and digests, it is necessary to also use the -v command.

As an example, using the 3 macros above, we have rpms built with:

  1. SHA256 digests for the header / body
  2. GPG Signatures using a SHA256 digest
  3. Legacy SHA1 / MD5 digests in the header

Using the CentOS version of rpm [RPM version 4.11.3]:

rpm --checksig RPMS/noarch/starlab-yum-s3-0.2.4-2.noarch.rpm

RPMS/noarch/starlab-yum-s3-0.2.4-2.noarch.rpm: rsa sha1 (md5) pgp md5 OK

With verbosity:

rpm --checksig -v RPMS/noarch/starlab-yum-s3-0.2.4-2.noarch.rpm 


    Header V3 RSA/SHA256 Signature, key ID 96966f38: OK

    Header SHA1 digest: OK (f0186daa7c8053aa25f89ce62e3cddebf7d6f7fc)

    V3 RSA/SHA256 Signature, key ID 96966f38: OK

    MD5 digest: OK (e794355904cda440ff820f7b6791ff4a)

Similarly, using the latest rpm [RPM version]:

/usr/local/bin/rpm --checksig RPMS/noarch/starlab-yum-s3-0.2.4-2.noarch.rpm

RPMS/noarch/starlab-yum-s3-0.2.4-2.noarch.rpm: digests signatures OK

With verbosity:

/usr/local/bin/rpm --checksig -v RPMS/noarch/starlab-yum-s3-0.2.4-2.noarch.rpm


    Header V3 RSA/SHA256 Signature, key ID 96966f38: OK

    Header SHA256 digest: OK

    Header SHA1 digest: OK

    Payload SHA256 digest: OK

    V3 RSA/SHA256 Signature, key ID 96966f38: OK

    MD5 digest: OK

In summary, there are 3 different macros that impact the ability to generate and verify SHA256 digests across rpm packages. Most of these macros are only available in upstream versions of rpm, and are not generally supported by shipping distributions that use the rpm format.