In this article, I will explain the process of signing RPM packages in Red Hat Enterprise Linux, RHEL 10.1 using cryptographic keys resistant to quantum computers yet to be developed in the near future. This is for developers and vendors interested in protecting their software with stronger signatures or achieving compliance. You will learn how to generate new OpenPGP keys, how to configure an rpm to use them, and how to integrate the steps into existing workflows.
What is post-quantum cryptography?
Post-quantum cryptography (PQC) is a common name for a set of algorithms believed to withstand the attacks of quantum computers when they will have enough computational power to break or weaken existing traditional cryptography algorithms used these days. They can be split into two groups: one used for key agreement, which is relevant to the online protocols like TLS; and the other used for signatures relevant to provide authenticity.
Over the last year, we’ve heard a lot about “harvest now/decrypt later” concerns that some agencies might be harvesting internet traffic with the hope to decrypt it later when quantum computers will be available. The signature algorithms group is especially relevant for software signatures made today, but we expect users to verify and trust them in 10 years, especially in IoT, embedded or enterprise systems, that are either not updated frequently or require long-term stability.
Signing RPM software
Red Hat ships the digitally signed software, which provides authenticity and integrity for the installed packages. For this use case, a private key signs the packages, and the system distributes a public key to the systems that install the software. This allows the user's machine to verify the software comes from Red Hat.
Historically, RPM contained just one OpenPGP signature, generally made by GnuPG. Over the last decades, this was usually an RSA key as it provides the widest compatibility with older versions of operating systems. In a world with PQC, we frequently see the use of hybrid keys and signatures. They are useful for the transition period, when we still do not fully trust the newly introduced PQC algorithms, but we do not want to depend solely on the legacy algorithms either.
RPM 6 (and backports currently in RHEL 10.1) introduces support for RPMv6 signatures. This new format makes it possible to include multiple signatures in each package, all of which you need to verify for the RPM to consider it trusted. Traditional RPMv4 signatures can co-exist with RPMv6 ones, allowing verification with older RPM versions too.
For signing software with PQC algorithms, we are using Sequoia-PGP tools, the same used internally by RPM to verify the signatures.
Generate PQC keys
To generate a new hybrid ML-DSA-87-Ed448 key pair using Sequoia, you can use the following command. Here we are using the profile RFC9580, saying that the key will be the OpenPGPv6, and ciphersuite mldsa87 is the parameter set approved for CNSA 2.0. Given that we are going to use the key only for signing, we can skip generating encryption and authentication subkeys.
Note: At this time, the PQC support is not available in upstream released versions, so the following will only work with RHEL 10.1 packages.
$ sq key generate --own-key --email vendor@example.com --name “Vendor Inc.” \
--cipher-suite mldsa87 --profile rfc9580 --cannot-authenticate \
--cannot-encrypt --expiration=neverTo verify the keys generated correctly, you can list them with the following command:
$ sq key listYou can export the OpenPGP certificate containing the public key to a file for distribution:
$ sq cert export --cert-email vendor@example.com > RPM-PGP-KEY-VENDORCurrently, there is also support for ML-DSA-65+Ed25519 ciphersuite, providing less security strength. The specification also provides support for SLH-DSA, but these are not implemented as of now.
Configuring RPM
Now that we have generated the key we want to use to sign RPMs, we need to configure the RPM to use the right key. This is usually done outside of the build system on a separate signing server. The RPM needs a couple of macros to support signing with sequoia available in a template file and enabled by copying the configuration file:
cp /usr/share/doc/rpm/macros.rpmsign-sequoia /etc/rpm/From the key listing in the previous section, we need to find the key fingerprint and write it into the ~/.rpmmacros file:
echo "%_gpg_name E4544AFB8685A1AD25025EF5B2[...]" > ~/.rpmmacrosNote: In Fedora or upstream RPM, the RPM configuration is slightly different, and configuring RPM to sign using Sequoia involves setting the following macros:
echo '%_openpgp_sign sq' > ~/.rpmmacros
echo ‘%_openpgp_sign_id E4544AFB8685A1AD25025EF5B2[...]’ >> ~/.rpmmacros Creating signatures
From now on, we can sign RPMs like we are used to signing. The only difference is the new switch, saying that the signature is a RPMv6 signature with the properties we discussed before, instead of the legacy RPMv4 one (default):
$ rpmsign --addsign --rpmv6 hello-2.0-1.x86_64.rpmMultiple signatures
The new RPMs can have multiple RPMv6 signatures. This means, you can repeat the previous steps multiple times to get an RPM signed with multiple different keys, achieving better security in case either of the keys get compromised or either of the cryptographic algorithms will be rendered insecure.
Additionally, the first signature that is compatible with the RPMv4 (generally made with OpenPGPv4 RSA key) is also stored as an RPMv4 signature. This allows the old RPM to trust the package signature, without the need to ship completely different packages for older systems or follow different signing procedures.
The following is an example of signing an RPM package with OpenPGPv4 RSA key, where the RPM writes two signatures: one RPMv6 compatible and one RPMv4 compatible, automatically.
Note: RPM 6 (and RHEL versions since 10.1) will ignore any RPMv4 signatures if there is at least one RPMv6 signature at verification time.
$ sq key generate --own-key --email vendor@example.com \
--name “Vendor Inc. (RSA)” --cipher-suite rsa4k --profile rfc4880 \
--cannot-authenticate --cannot-encrypt
$ sq key list
$ echo "%_gpg_name 1bba42f5e63db592588d5bd655264a6f8c83[...]" > ~/.rpmmacros
$ rpmsign --addsign --rpmv6 hello-2.0-1.x86_64.rpmImporting keys
The keys are imported on the target systems the same way as any other key in the past. Generally, the repository will have the gpgkey option with a comma separated list of keys (either local or remote). The user verifies the import of the key once they install the first package from this repository.
You can also manually import the key with the following:
$ rpmkeys --import RPM-PGP-KEY-VENDORIn either way, the users are required to verify the key fingerprint matches the vendor provided information through an independent channel.
Note: The keys generated with these steps consist of the primary key and signing subkey. Importing these keys will work only in RHEL10 as the old RPM does not have support for subkeys. To generate primary keys with signing capabilities requires patching sequoia-sq at the moment. We are working to improve this in the future.
Verifying signatures
Once the signing keys are imported, RPM and DNF will verify the signatures during package installation automatically. You can invoke RPM manually to verify signatures on binary RPMs with rpmkeys, and the output can look like this:
$ rpmkeys -Kv hello-2.0-1.x86_64.rpm
hello-2.0-1.x86_64.rpm:
Header OpenPGP V6 ML-DSA-87+Ed448/SHA512 signature, key fingerprint: 4f99a80d57422e7a8182755ee6bef5ee40be54adcd6e98c15034ae77c0603fe8: OK
Header OpenPGP V4 RSA/SHA512 signature, key fingerprint: 1bba42f5e63db592588d5bd655264a6f8c83e13e: OK
Header SHA256 digest: OK
Payload SHA256 digest: OKThe same RPM will look differently on older systems not supporting the RPMv6 signatures. But given that it was signed with the OpenPGPv4 RSA key and the RPMv4 signature was included, as previously discussed, we can see the package as signed too:
$ rpmkeys -Kv hello-2.0-1.x86_64.rpm
hello-2.0-1.x86_64.rpm:
Header V4 RSA/SHA512 Signature, key ID 8c83e13e: OK
Header SHA256 digest: OK
Header SHA1 digest: OK
Payload SHA256 digest: OK
MD5 digest: OKIf either of the signatures is not successfully verified, the whole RPM verification fails, and installation is not allowed. Failed verification of the signatures could be due to: corruption, tampering with the package, cryptographic policy not allowing some algorithms, or the signing key is not imported.
Final thoughts
This article has demonstrated how to sign RPM packages in RHEL 10.1 using quantum-resistant cryptography, highlighting the generation of hybrid ML-DSA-87-Ed448 keys, configuring RPM for Sequoia-PGP, and creating multiple signatures for enhanced security. By adopting these methods, developers and vendors can future-proof their software against quantum attacks and ensure long-term authenticity and integrity. Start considering how implementing these advanced cryptographic practices can secure your software.