[PATCH v2 00/14] KEYS: Add support for PGP keys and signatures

2024-08-18 Thread Roberto Sassu
From: Roberto Sassu 

Support for PGP keys and signatures was proposed by David long time ago,
before the decision of using PKCS#7 for kernel modules signatures
verification was made. After that, there has been not enough interest to
support PGP too.

Lately, when discussing a proposal of introducing fsverity signatures in
Fedora [1], developers expressed their preference on not having a separate
key for signing, which would complicate the management of the distribution.
They would be more in favor of using the same PGP key, currently used for
signing RPM headers, also for file-based signatures (not only fsverity, but
also IMA ones).

Another envisioned use case would be to add the ability to appraise RPM
headers with their existing PGP signature, so that they can be used as an
authenticated source of reference values for appraising remaining
files [2].

To make these use cases possible, introduce support for PGP keys and
signatures in the kernel, and load provided PGP keys in the built-in
keyring, so that PGP signatures of RPM headers, fsverity digests, and IMA
digests can be verified from this trust anchor.

In addition to the original version of the patch set, also introduce
support for signature verification of PGP keys, so that those keys can be
added to keyrings with a signature-based restriction (e.g. .ima). PGP keys
are searched with partial IDs, provided with signature subtype 16 (Issuer).
Search with full IDs could be supported with
draft-ietf-openpgp-rfc4880bis-10, by retrieving the information from
signature subtype 33 (Issuer Fingerprint). Due to the possibility of ID
collisions, the key_or_keyring restriction is not supported.

The patch set includes two preliminary patches: patch 1 introduces
mpi_key_length(), to get the number of bits and bytes of an MPI; patch 2
introduces rsa_parse_priv_key_raw() and rsa_parse_pub_key_raw(), to parse
an RSA key in RAW format if the ASN.1 parser returns an error.

Patches 3-5 introduce the library necessary to parse PGP keys and
signatures, whose support is added with patches 6-10. Patch 11 introduces
verify_pgp_signature() to be used by kernel subsystems (e.g. fsverity and
IMA). Patch 12 is for testing of PGP signatures. Finally, patches 13-14
allow loading a set of PGP keys from a supplied blob at boot time.

Changelog

v1 [4]:
- Remove quiet_cmd_extract_certs (redundant, likely leftover from
  conflict resolution)
- Load PGP keys embedded in the kernel image within load_module_cert()
  and load_system_certificate_list(), instead of using a separate initcall
- Style bug fixes found by checkpatch.pl
- Add  include in crypto/asymmetric_keys/pgp_preload.c, to
  remove no previous prototype warning
- Correctly check returned tfm in pgp_generate_fingerprint()
- Fix printing message in pgp_generate_fingerprint()
- Don't create a public key if the key blob does not contain a PGP key
  packet
- Remove unused pgp_pubkey_hash array
- Set KEY_EFLAG_DIGITALSIG key flag if the key has the capability
- Allow PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY signature type (for key sigs)
- Add is_key_sig parameter to pgp_sig_get_sig() to ensure the key
  signature type is PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY or
  PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY

v0 [3]:
- style fixes
- move include/linux/pgp.h and pgplib.h to crypto/asymmetric_keys
- introduce verify_pgp_signature()
- replace KEY_ALLOC_TRUSTED flag with KEY_ALLOC_BUILT_IN
- don't fetch PGP subkeys
- drop support for DSA
- store number of MPIs in pgp_key_algo_p_num_mpi array
- replace dynamic memory allocations with static ones in
  pgp_generate_fingerprint()
- store only keys with capability of verifying signatures
- remember selection of PGP signature packet and don't repeat parsing
- move search of the PGP key to verify the signature from the beginning
  to the end of the verification process (to be similar with PKCS#7)
- don't retry key search in the session keyring from the signature
  verification code, let the caller pass the desired keyring
- for the PGP signature test key type, retry the key search in the session
  keyring
- retry key search in restrict_link_by_signature() with a partial ID
  (provided in the PGP signature)

[1] https://fedoraproject.org/wiki/Changes/FsVerityRPM
[2] 
https://lore.kernel.org/linux-integrity/20240415142436.2545003-1-roberto.sa...@huaweicloud.com/
[3] 
https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-modsign.git/log/?h=pgp-parser
[4] 
https://lore.kernel.org/linux-integrity/2022080318.591029-1-roberto.sa...@huawei.com/

David Howells (8):
  PGPLIB: PGP definitions (RFC 4880)
  PGPLIB: Basic packet parser
  PGPLIB: Signature parser
  KEYS: PGP data parser
  KEYS: Provide PGP key description autogeneration
  KEYS: PGP-based public key signature verification
  PGP: Provide a key type for testing PGP signatures
  KEYS: Provide a function to load keys from a PGP keyring blob

Roberto Sassu (6):
  mpi: Introduce mpi_key_length()
  rsa: add parser of raw format
  KEYS: Retry asym k

[PATCH v2 02/14] rsa: add parser of raw format

2024-08-18 Thread Roberto Sassu
From: Roberto Sassu 

Parse the RSA key with RAW format if the ASN.1 parser returns an error.

Signed-off-by: Roberto Sassu 
Signed-off-by: David Howells 
---
 crypto/rsa.c  | 14 +--
 crypto/rsa_helper.c   | 69 +++
 include/crypto/internal/rsa.h |  6 +++
 3 files changed, 85 insertions(+), 4 deletions(-)

diff --git a/crypto/rsa.c b/crypto/rsa.c
index d9be9e86097e..66d42974d47d 100644
--- a/crypto/rsa.c
+++ b/crypto/rsa.c
@@ -272,8 +272,11 @@ static int rsa_set_pub_key(struct crypto_akcipher *tfm, 
const void *key,
rsa_free_mpi_key(mpi_key);
 
ret = rsa_parse_pub_key(&raw_key, key, keylen);
-   if (ret)
-   return ret;
+   if (ret) {
+   ret = rsa_parse_pub_key_raw(&raw_key, key, keylen);
+   if (ret)
+   return ret;
+   }
 
mpi_key->e = mpi_read_raw_data(raw_key.e, raw_key.e_sz);
if (!mpi_key->e)
@@ -311,8 +314,11 @@ static int rsa_set_priv_key(struct crypto_akcipher *tfm, 
const void *key,
rsa_free_mpi_key(mpi_key);
 
ret = rsa_parse_priv_key(&raw_key, key, keylen);
-   if (ret)
-   return ret;
+   if (ret) {
+   ret = rsa_parse_priv_key_raw(&raw_key, key, keylen);
+   if (ret)
+   return ret;
+   }
 
mpi_key->d = mpi_read_raw_data(raw_key.d, raw_key.d_sz);
if (!mpi_key->d)
diff --git a/crypto/rsa_helper.c b/crypto/rsa_helper.c
index 94266f29049c..fb9443df8f0b 100644
--- a/crypto/rsa_helper.c
+++ b/crypto/rsa_helper.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include "rsapubkey.asn1.h"
 #include "rsaprivkey.asn1.h"
@@ -148,6 +149,32 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned 
char tag,
return 0;
 }
 
+typedef int (*rsa_get_func)(void *, size_t, unsigned char,
+   const void *, size_t);
+
+static int rsa_parse_key_raw(struct rsa_key *rsa_key,
+const void *key, unsigned int key_len,
+rsa_get_func *func, int n_func)
+{
+   unsigned int nbytes, len = key_len;
+   const void *key_ptr = key;
+   int ret, i;
+
+   for (i = 0; i < n_func; i++) {
+   ret = mpi_key_length(key_ptr, len, NULL, &nbytes);
+   if (ret < 0)
+   return ret;
+
+   ret = func[i](rsa_key, 0, 0, key_ptr + 2, nbytes);
+   if (ret < 0)
+   return ret;
+
+   key_ptr += nbytes + 2;
+   }
+
+   return (key_ptr == key + key_len) ? 0 : -EINVAL;
+}
+
 /**
  * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the
  *   provided struct rsa_key, pointers to the raw key as 
is,
@@ -166,6 +193,27 @@ int rsa_parse_pub_key(struct rsa_key *rsa_key, const void 
*key,
 }
 EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
 
+/**
+ * rsa_parse_pub_key_raw() - parse the RAW key and store in the provided struct
+ *   rsa_key, pointers to the raw key as is, so that
+ *   the caller can copy it or MPI parse it, etc.
+ *
+ * @rsa_key:   struct rsa_key key representation
+ * @key:   key in RAW format
+ * @key_len:   length of key
+ *
+ * Return: 0 on success or error code in case of error
+ */
+int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key,
+ unsigned int key_len)
+{
+   rsa_get_func pub_func[] = {rsa_get_n, rsa_get_e};
+
+   return rsa_parse_key_raw(rsa_key, key, key_len,
+pub_func, ARRAY_SIZE(pub_func));
+}
+EXPORT_SYMBOL_GPL(rsa_parse_pub_key_raw);
+
 /**
  * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the
  *provided struct rsa_key, pointers to the raw key
@@ -184,3 +232,24 @@ int rsa_parse_priv_key(struct rsa_key *rsa_key, const void 
*key,
return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len);
 }
 EXPORT_SYMBOL_GPL(rsa_parse_priv_key);
+
+/**
+ * rsa_parse_priv_key_raw() - parse the RAW key and store in the provided 
struct
+ *rsa_key, pointers to the raw key as is, so that
+ *the caller can copy it or MPI parse it, etc.
+ *
+ * @rsa_key:   struct rsa_key key representation
+ * @key:   key in RAW format
+ * @key_len:   length of key
+ *
+ * Return: 0 on success or error code in case of error
+ */
+int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key,
+  unsigned int key_len)
+{
+   rsa_get_func priv_func[] = {rsa_get_n, rsa_get_e, rsa_get_d};
+
+   return rsa_parse_key_raw(rsa_key, key, key_len,
+priv_func, ARRAY_SIZE(priv_func));
+}
+EXPORT_SYMBOL_GPL(rsa_parse_priv_key_raw);
diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h
index e870133f4b77..7141e806ce

[PATCH v2 01/14] mpi: Introduce mpi_key_length()

2024-08-18 Thread Roberto Sassu
From: Roberto Sassu 

Introduce the new function to get the number of bits and bytes from an MPI.

Signed-off-by: Roberto Sassu 
Signed-off-by: David Howells 
---
 include/linux/mpi.h   |  2 ++
 lib/crypto/mpi/mpicoder.c | 33 ++---
 2 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/include/linux/mpi.h b/include/linux/mpi.h
index eb0d1c1db208..a7dd4c9d8120 100644
--- a/include/linux/mpi.h
+++ b/include/linux/mpi.h
@@ -90,6 +90,8 @@ enum gcry_mpi_format {
 };
 
 MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes);
+int mpi_key_length(const void *xbuffer, unsigned int ret_nread,
+  unsigned int *nbits_arg, unsigned int *nbytes_arg);
 MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread);
 int mpi_fromstr(MPI val, const char *str);
 MPI mpi_scanval(const char *string);
diff --git a/lib/crypto/mpi/mpicoder.c b/lib/crypto/mpi/mpicoder.c
index 3cb6bd148fa9..92447a1c8bf9 100644
--- a/lib/crypto/mpi/mpicoder.c
+++ b/lib/crypto/mpi/mpicoder.c
@@ -79,22 +79,41 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes)
 }
 EXPORT_SYMBOL_GPL(mpi_read_raw_data);
 
-MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread)
+int mpi_key_length(const void *xbuffer, unsigned int ret_nread,
+  unsigned int *nbits_arg, unsigned int *nbytes_arg)
 {
const uint8_t *buffer = xbuffer;
-   unsigned int nbits, nbytes;
-   MPI val;
+   unsigned int nbits;
 
-   if (*ret_nread < 2)
-   return ERR_PTR(-EINVAL);
+   if (ret_nread < 2)
+   return -EINVAL;
nbits = buffer[0] << 8 | buffer[1];
 
if (nbits > MAX_EXTERN_MPI_BITS) {
pr_info("MPI: mpi too large (%u bits)\n", nbits);
-   return ERR_PTR(-EINVAL);
+   return -EINVAL;
}
 
-   nbytes = DIV_ROUND_UP(nbits, 8);
+   if (nbits_arg)
+   *nbits_arg = nbits;
+   if (nbytes_arg)
+   *nbytes_arg = DIV_ROUND_UP(nbits, 8);
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(mpi_key_length);
+
+MPI mpi_read_from_buffer(const void *xbuffer, unsigned int *ret_nread)
+{
+   const uint8_t *buffer = xbuffer;
+   unsigned int nbytes;
+   MPI val;
+   int ret;
+
+   ret = mpi_key_length(xbuffer, *ret_nread, NULL, &nbytes);
+   if (ret < 0)
+   return ERR_PTR(ret);
+
if (nbytes + 2 > *ret_nread) {
pr_info("MPI: mpi larger than buffer nbytes=%u ret_nread=%u\n",
nbytes, *ret_nread);
-- 
2.34.1




[PATCH v2 03/14] PGPLIB: PGP definitions (RFC 4880)

2024-08-18 Thread Roberto Sassu
From: David Howells 

Provide some useful PGP definitions from RFC 4880.  These describe details
of public key crypto as used by crypto keys for things like signature
verification.

Signed-off-by: David Howells 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/pgp.h | 206 +++
 1 file changed, 206 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp.h

diff --git a/crypto/asymmetric_keys/pgp.h b/crypto/asymmetric_keys/pgp.h
new file mode 100644
index ..f8f06b674462
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp.h
@@ -0,0 +1,206 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* PGP definitions (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ */
+
+#include 
+
+struct pgp_key_ID {
+   u8 id[8];
+} __packed;
+
+struct pgp_time {
+   u8 time[4];
+} __packed;
+
+/*
+ * PGP public-key algorithm identifiers [RFC4880: 9.1]
+ */
+enum pgp_pubkey_algo {
+   PGP_PUBKEY_RSA_ENC_OR_SIG   = 1,
+   PGP_PUBKEY_RSA_ENC_ONLY = 2,
+   PGP_PUBKEY_RSA_SIG_ONLY = 3,
+   PGP_PUBKEY_ELGAMAL  = 16,
+   PGP_PUBKEY_DSA  = 17,
+   PGP_PUBKEY__LAST
+};
+
+/*
+ * PGP symmetric-key algorithm identifiers [RFC4880: 9.2]
+ */
+enum pgp_symkey_algo {
+   PGP_SYMKEY_PLAINTEXT= 0,
+   PGP_SYMKEY_IDEA = 1,
+   PGP_SYMKEY_3DES = 2,
+   PGP_SYMKEY_CAST5= 3,
+   PGP_SYMKEY_BLOWFISH = 4,
+   PGP_SYMKEY_AES_128KEY   = 7,
+   PGP_SYMKEY_AES_192KEY   = 8,
+   PGP_SYMKEY_AES_256KEY   = 9,
+   PGP_SYMKEY_TWOFISH_256KEY   = 10,
+};
+
+/*
+ * PGP compression algorithm identifiers [RFC4880: 9.3]
+ */
+enum pgp_compr_algo {
+   PGP_COMPR_UNCOMPRESSED  = 0,
+   PGP_COMPR_ZIP   = 1,
+   PGP_COMPR_ZLIB  = 2,
+   PGP_COMPR_BZIP2 = 3,
+};
+
+/*
+ * PGP hash algorithm identifiers [RFC4880: 9.4]
+ */
+enum pgp_hash_algo {
+   PGP_HASH_MD5= 1,
+   PGP_HASH_SHA1   = 2,
+   PGP_HASH_RIPE_MD_160= 3,
+   PGP_HASH_SHA256 = 8,
+   PGP_HASH_SHA384 = 9,
+   PGP_HASH_SHA512 = 10,
+   PGP_HASH_SHA224 = 11,
+   PGP_HASH__LAST
+};
+
+extern const char *const pgp_hash_algorithms[PGP_HASH__LAST];
+
+/*
+ * PGP packet type tags [RFC4880: 4.3].
+ */
+enum pgp_packet_tag {
+   PGP_PKT_RESERVED= 0,
+   PGP_PKT_PUBKEY_ENC_SESSION_KEY  = 1,
+   PGP_PKT_SIGNATURE   = 2,
+   PGP_PKT_SYMKEY_ENC_SESSION_KEY  = 3,
+   PGP_PKT_ONEPASS_SIGNATURE   = 4,
+   PGP_PKT_SECRET_KEY  = 5,
+   PGP_PKT_PUBLIC_KEY  = 6,
+   PGP_PKT_SECRET_SUBKEY   = 7,
+   PGP_PKT_COMPRESSED_DATA = 8,
+   PGP_PKT_SYM_ENC_DATA= 9,
+   PGP_PKT_MARKER  = 10,
+   PGP_PKT_LITERAL_DATA= 11,
+   PGP_PKT_TRUST   = 12,
+   PGP_PKT_USER_ID = 13,
+   PGP_PKT_PUBLIC_SUBKEY   = 14,
+   PGP_PKT_USER_ATTRIBUTE  = 17,
+   PGP_PKT_SYM_ENC_AND_INTEG_DATA  = 18,
+   PGP_PKT_MODIFY_DETECT_CODE  = 19,
+   PGP_PKT_PRIVATE_0   = 60,
+   PGP_PKT_PRIVATE_3   = 63,
+   PGP_PKT__HIGHEST= 63
+};
+
+/*
+ * Signature (tag 2) packet [RFC4880: 5.2].
+ */
+enum pgp_signature_version {
+   PGP_SIG_VERSION_3   = 3,
+   PGP_SIG_VERSION_4   = 4,
+};
+
+enum pgp_signature_type {
+   PGP_SIG_BINARY_DOCUMENT_SIG = 0x00,
+   PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG = 0x01,
+   PGP_SIG_STANDALONE_SIG  = 0x02,
+   PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY  = 0x10,
+   PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY = 0x11,
+   PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY   = 0x12,
+   PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY= 0x13,
+   PGP_SIG_SUBKEY_BINDING_SIG  = 0x18,
+   PGP_SIG_PRIMARY_KEY_BINDING_SIG = 0x19,
+   PGP_SIG_DIRECTLY_ON_KEY = 0x1F,
+   PGP_SIG_KEY_REVOCATION_SIG  = 0x20,
+   PGP_SIG_SUBKEY_REVOCATION_SIG   = 0x28,
+   PGP_SIG_CERT_REVOCATION_SIG = 0x30,
+   PGP_SIG_TIMESTAMP_SIG   = 0x40,
+   PGP_SIG_THIRD_PARTY_CONFIRM_SIG = 0x50,
+};
+
+struct pgp_signature_v3_packet {
+   enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */
+   u8  length_of_hashed;   /* == 5 */
+   struct {
+   enum pgp_signature_type signature_type : 8;
+   struct pgp_time creation_time;
+   } __packed hashed;
+   struct pgp_key_ID 

[PATCH v2 04/14] PGPLIB: Basic packet parser

2024-08-18 Thread Roberto Sassu
From: David Howells 

Provide a simple parser that extracts the packets from a PGP packet blob
and passes the desirous ones to the given processor function:

struct pgp_parse_context {
u64 types_of_interest;
int (*process_packet)(struct pgp_parse_context *context,
  enum pgp_packet_tag type,
  u8 headerlen,
  const u8 *data,
  size_t datalen);
};

int pgp_parse_packets(const u8 *data, size_t datalen,
  struct pgp_parse_context *ctx);

This is configured on with CONFIG_PGP_LIBRARY.

Signed-off-by: David Howells 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/Kconfig   |   6 +
 crypto/asymmetric_keys/Makefile  |   5 +
 crypto/asymmetric_keys/pgp_library.c | 272 +++
 crypto/asymmetric_keys/pgplib.h  |  33 
 4 files changed, 316 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp_library.c
 create mode 100644 crypto/asymmetric_keys/pgplib.h

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index e1345b8f39f1..ce17c0881d94 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -103,4 +103,10 @@ config FIPS_SIGNATURE_SELFTEST_ECDSA
depends on CRYPTO_SHA256=y || CRYPTO_SHA256=FIPS_SIGNATURE_SELFTEST
depends on CRYPTO_ECDSA=y || CRYPTO_ECDSA=FIPS_SIGNATURE_SELFTEST
 
+config PGP_LIBRARY
+   tristate "PGP parsing library"
+   help
+ This option enables a library that provides a number of simple
+ utility functions for parsing PGP (RFC 4880) packet-based messages.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index bc65d3b98dcb..055b28207111 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -79,3 +79,8 @@ verify_signed_pefile-y := \
 
 $(obj)/mscode_parser.o: $(obj)/mscode.asn1.h $(obj)/mscode.asn1.h
 $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h
+
+#
+# PGP handling
+#
+obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
diff --git a/crypto/asymmetric_keys/pgp_library.c 
b/crypto/asymmetric_keys/pgp_library.c
new file mode 100644
index ..33ed01f67654
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/* PGP packet parser (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGPL: "fmt
+#include 
+#include 
+#include 
+
+#include "pgplib.h"
+
+MODULE_LICENSE("GPL");
+
+const char *const pgp_hash_algorithms[PGP_HASH__LAST] = {
+   [PGP_HASH_MD5]  = "md5",
+   [PGP_HASH_SHA1] = "sha1",
+   [PGP_HASH_RIPE_MD_160]  = "rmd160",
+   [PGP_HASH_SHA256]   = "sha256",
+   [PGP_HASH_SHA384]   = "sha384",
+   [PGP_HASH_SHA512]   = "sha512",
+   [PGP_HASH_SHA224]   = "sha224",
+};
+EXPORT_SYMBOL_GPL(pgp_hash_algorithms);
+
+/**
+ * pgp_parse_packet_header - Parse a PGP packet header
+ * @_data: Start of the PGP packet (updated to PGP packet data)
+ * @_datalen: Amount of data remaining in buffer (decreased)
+ * @_type: Where the packet type will be returned
+ * @_headerlen: Where the header length will be returned
+ *
+ * Parse a set of PGP packet header [RFC 4880: 4.2].
+ *
+ * Return: packet data size on success; non-zero on error.  If successful,
+ * *_data and *_datalen will have been updated and *_headerlen will be set to
+ * hold the length of the packet header.
+ */
+static ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen,
+  enum pgp_packet_tag *_type,
+  u8 *_headerlen)
+{
+   enum pgp_packet_tag type;
+   const u8 *data = *_data;
+   size_t size, datalen = *_datalen;
+
+   pr_devel("-->%s(,%zu,,)\n", __func__, datalen);
+
+   if (datalen < 2)
+   goto short_packet;
+
+   pr_devel("pkthdr %02x, %02x\n", data[0], data[1]);
+
+   type = *data++;
+   datalen--;
+   if (!(type & 0x80)) {
+   pr_debug("Packet type does not have MSB set\n");
+   return -EBADMSG;
+   }
+   type &= ~0x80;
+
+   if (type & 0x40) {
+   /* New packet length format */
+   type &= ~0x40;
+   pr_devel("new format: t=%u\n", type);
+   switch (data[0]) {
+   case 0x00 ... 0xbf:
+   /* One-byte length */
+   size = data[0];
+   data++;
+   datalen--;
+   *_headerlen = 2;
+   break;
+   case 0xc0

[PATCH v2 05/14] PGPLIB: Signature parser

2024-08-18 Thread Roberto Sassu
From: David Howells 

Provide some PGP signature parsing helpers:

 (1) A function to parse V4 signature subpackets and pass the desired ones
 to a processor function:

int pgp_parse_sig_subpkts(const u8 *data, size_t datalen,
  struct pgp_parse_sig_context *ctx);

 (2) A function to parse out basic signature parameters from any PGP
 signature such that the algorithms and public key can be selected:

int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
 struct pgp_sig_parameters *p);

Signed-off-by: David Howells 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/pgp_library.c | 284 +++
 crypto/asymmetric_keys/pgplib.h  |  25 +++
 2 files changed, 309 insertions(+)

diff --git a/crypto/asymmetric_keys/pgp_library.c 
b/crypto/asymmetric_keys/pgp_library.c
index 33ed01f67654..2fd3e7d55f6f 100644
--- a/crypto/asymmetric_keys/pgp_library.c
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -270,3 +270,287 @@ int pgp_parse_public_key(const u8 **_data, size_t 
*_datalen,
return 0;
 }
 EXPORT_SYMBOL_GPL(pgp_parse_public_key);
+
+/**
+ * pgp_parse_sig_subpkt_header - Parse a PGP V4 signature subpacket header
+ * @_data: Start of the subpacket (updated to subpacket data)
+ * @_datalen: Amount of data remaining in buffer (decreased)
+ * @_type: Where the subpacket type will be returned
+ *
+ * Parse a PGP V4 signature subpacket header [RFC 4880: 5.2.3.1].
+ *
+ * Return: packet data size on success; non-zero on error.  If successful,
+ * *_data and *_datalen will have been updated and *_headerlen will be set to
+ * hold the length of the packet header.
+ */
+static ssize_t pgp_parse_sig_subpkt_header(const u8 **_data, size_t *_datalen,
+  enum pgp_sig_subpkt_type *_type)
+{
+   enum pgp_sig_subpkt_type type;
+   const u8 *data = *_data;
+   size_t size, datalen = *_datalen;
+
+   pr_devel("-->%s(,%zu,,)\n", __func__, datalen);
+
+   if (datalen < 2)
+   goto short_subpacket;
+
+   pr_devel("subpkt hdr %02x, %02x\n", data[0], data[1]);
+
+   switch (data[0]) {
+   case 0x00 ... 0xbf:
+   /* One-byte length */
+   size = data[0];
+   data++;
+   datalen--;
+   break;
+   case 0xc0 ... 0xfe:
+   /* Two-byte length */
+   if (datalen < 3)
+   goto short_subpacket;
+   size = (data[0] - 192) * 256;
+   size += data[1] + 192;
+   data += 2;
+   datalen -= 2;
+   break;
+   case 0xff:
+   if (datalen < 6)
+   goto short_subpacket;
+   size  = data[1] << 24;
+   size |= data[2] << 16;
+   size |= data[3] << 8;
+   size |= data[4];
+   data += 5;
+   datalen -= 5;
+   break;
+   }
+
+   /* The type octet is included in the size */
+   pr_devel("datalen=%zu size=%zu\n", datalen, size);
+   if (datalen < size)
+   goto short_subpacket;
+   if (size == 0)
+   goto very_short_subpacket;
+   if (size > INT_MAX)
+   goto too_big;
+
+   type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK;
+   datalen--;
+   size--;
+
+   *_data = data;
+   *_datalen = datalen;
+   *_type = type;
+   pr_devel("Found subpkt type=%u size=%zd\n", type, size);
+   return size;
+
+very_short_subpacket:
+   pr_debug("Signature subpacket size can't be zero\n");
+   return -EBADMSG;
+short_subpacket:
+   pr_debug("Attempt to parse short signature subpacket\n");
+   return -EBADMSG;
+too_big:
+   pr_debug("Signature subpacket size >2G\n");
+   return -EMSGSIZE;
+}
+
+/**
+ * pgp_parse_sig_subpkts - Parse a set of PGP V4 signatute subpackets
+ * @data: Data to be parsed (updated)
+ * @datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP signature subpackets [RFC 4880: 5.2.3].
+ *
+ * Return: 0 on successful parsing, an error value otherwise
+ */
+static int pgp_parse_sig_subpkts(const u8 *data, size_t datalen,
+struct pgp_parse_sig_context *ctx)
+{
+   enum pgp_sig_subpkt_type type;
+   ssize_t pktlen;
+   int ret;
+
+   pr_devel("-->%s(,%zu,,)\n", __func__, datalen);
+
+   while (datalen > 2) {
+   pktlen = pgp_parse_sig_subpkt_header(&data, &datalen, &type);
+   if (pktlen < 0)
+   return pktlen;
+   if (test_bit(type, ctx->types_of_interest)) {
+   ret = ctx->process_packet(ctx, type, data, pktlen);
+   if (ret < 0)
+   return ret;
+   }
+   data += pktlen;
+   datalen -= 

[PATCH v2 06/14] KEYS: PGP data parser

2024-08-18 Thread Roberto Sassu
From: David Howells 

Implement a PGP data parser for the crypto key type to use when
instantiating a key.

This parser attempts to parse the instantiation data as a PGP packet
sequence (RFC 4880) and if it parses okay, attempts to extract a public-key
algorithm key or subkey from it.

If it finds such a key, it will set up a public_key subtype payload with
appropriate handler routines (RSA) and attach it to the key.

Thanks to Tetsuo Handa  for pointing
out some errors.

Signed-off-by: David Howells 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/Kconfig  |  11 +
 crypto/asymmetric_keys/Makefile |   4 +
 crypto/asymmetric_keys/pgp_parser.h |  18 ++
 crypto/asymmetric_keys/pgp_public_key.c | 366 
 4 files changed, 399 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp_parser.h
 create mode 100644 crypto/asymmetric_keys/pgp_public_key.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index ce17c0881d94..4c5c52cf54e4 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -109,4 +109,15 @@ config PGP_LIBRARY
  This option enables a library that provides a number of simple
  utility functions for parsing PGP (RFC 4880) packet-based messages.
 
+config PGP_KEY_PARSER
+   tristate "PGP key parser"
+   depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+   select PGP_LIBRARY
+   select MD5 # V3 fingerprint generation
+   select SHA1 # V4 fingerprint generation
+   help
+ This option provides support for parsing PGP (RFC 4880) format blobs
+ for key data and provides the ability to instantiate a crypto key
+ from a public key packet found inside the blob.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 055b28207111..d197e8b23b83 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -84,3 +84,7 @@ $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c 
$(obj)/mscode.asn1.h
 # PGP handling
 #
 obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+
+obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o
+pgp_key_parser-y := \
+   pgp_public_key.o
diff --git a/crypto/asymmetric_keys/pgp_parser.h 
b/crypto/asymmetric_keys/pgp_parser.h
new file mode 100644
index ..900f81f5ee14
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_parser.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* PGP crypto data parser internal definitions
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ */
+
+#include "pgplib.h"
+
+#define kenter(FMT, ...) \
+   pr_devel("==> %s(" FMT ")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+   pr_devel("<== %s()" FMT "\n", __func__, ##__VA_ARGS__)
+
+/*
+ * pgp_public_key.c
+ */
+extern const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST];
diff --git a/crypto/asymmetric_keys/pgp_public_key.c 
b/crypto/asymmetric_keys/pgp_public_key.c
new file mode 100644
index ..cb399f5cdd3e
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Instantiate a public key crypto key from PGP format data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGP: "fmt
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pgp_parser.h"
+
+#define MAX_MPI 5
+#define KEYCTL_SUPPORTS_ENCDEC \
+   (KEYCTL_SUPPORTS_ENCRYPT | KEYCTL_SUPPORTS_DECRYPT)
+#define KEYCTL_SUPPORTS_SIGVER (KEYCTL_SUPPORTS_SIGN | KEYCTL_SUPPORTS_VERIFY)
+
+MODULE_LICENSE("GPL");
+
+const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST] = {
+   [PGP_PUBKEY_RSA_ENC_OR_SIG] = "rsa",
+   [PGP_PUBKEY_RSA_ENC_ONLY]   = "rsa",
+   [PGP_PUBKEY_RSA_SIG_ONLY]   = "rsa",
+   [PGP_PUBKEY_ELGAMAL]= NULL,
+   [PGP_PUBKEY_DSA]= NULL,
+};
+
+static const int pgp_key_algo_p_num_mpi[PGP_PUBKEY__LAST] = {
+   [PGP_PUBKEY_RSA_ENC_OR_SIG] = 2,
+   [PGP_PUBKEY_RSA_ENC_ONLY]   = 2,
+   [PGP_PUBKEY_RSA_SIG_ONLY]   = 2,
+   [PGP_PUBKEY_ELGAMAL]= 3,
+   [PGP_PUBKEY_DSA]= 4,
+};
+
+static const u8 pgp_public_key_capabilities[PGP_PUBKEY__LAST] = {
+   [PGP_PUBKEY_RSA_ENC_OR_SIG] = KEYCTL_SUPPORTS_ENCDEC |
+ KEYCTL_SUPPORTS_SIGVER,
+   [PGP_PUBKEY_RSA_ENC_ONLY]   = KEYCTL_SUPPORTS_ENCDEC,
+   [PGP_PUBKEY_RSA_SIG_ONLY]   = KEYCTL_SUPPORTS_SIGVER,
+   [PGP_PUBKEY_ELGAMAL]= 0,
+   [PGP_PUBKEY_DSA]= 0,
+};
+
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
+{
+   crypto_shash_update(digest, &ch, 1);
+}
+
+struct pgp_key_data_parse_context {
+   

[PATCH v2 07/14] KEYS: Provide PGP key description autogeneration

2024-08-18 Thread Roberto Sassu
From: David Howells 

Provide a facility to autogenerate the name of PGP keys from the contents
of the payload.  If add_key() is given a blank description, a description
is constructed from the last user ID packet in the payload data plus the
last 8 hex digits of the key ID.  For instance:

keyctl padd asymmetric "" @s 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/pgp_public_key.c | 47 -
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/crypto/asymmetric_keys/pgp_public_key.c 
b/crypto/asymmetric_keys/pgp_public_key.c
index cb399f5cdd3e..e7f74d513a9e 100644
--- a/crypto/asymmetric_keys/pgp_public_key.c
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -59,6 +59,8 @@ struct pgp_key_data_parse_context {
struct public_key *pub;
u8 raw_fingerprint[HASH_MAX_DIGESTSIZE];
size_t raw_fingerprint_len;
+   const char *user_id;
+   size_t user_id_len;
 };
 
 /*
@@ -210,6 +212,15 @@ static int pgp_process_public_key(struct pgp_parse_context 
*context,
 
kenter(",%u,%u,,%zu", type, headerlen, datalen);
 
+   if (type == PGP_PKT_USER_ID) {
+   if (!ctx->user_id_len) {
+   ctx->user_id = data;
+   ctx->user_id_len = datalen;
+   }
+   kleave(" = 0 [user ID]");
+   return 0;
+   }
+
if (ctx->raw_fingerprint_len) {
kleave(" = -ENOKEY [already]");
return -EBADMSG;
@@ -317,7 +328,8 @@ static int pgp_key_parse(struct key_preparsed_payload *prep)
kenter("");
 
memset(&ctx, 0, sizeof(ctx));
-   ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY);
+   ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY) |
+   (1 << PGP_PKT_USER_ID);
ctx.pgp.process_packet = pgp_process_public_key;
 
ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp);
@@ -330,6 +342,39 @@ static int pgp_key_parse(struct key_preparsed_payload 
*prep)
goto error;
}
 
+   if (ctx.user_id && ctx.user_id_len > 0) {
+   /*
+* Propose a description for the key (user ID without the
+* comment).
+*/
+   size_t ulen = ctx.user_id_len;
+   const char *p;
+
+   p = memchr(ctx.user_id, '(', ulen);
+   if (p) {
+   /* Remove the comment */
+   do {
+   p--;
+   } while (*p == ' ' && p > ctx.user_id);
+   if (*p != ' ')
+   p++;
+   ulen = p - ctx.user_id;
+   }
+
+   if (ulen > 255 - 9)
+   ulen = 255 - 9;
+   prep->description = kmalloc(ulen + 1 + 8 + 1, GFP_KERNEL);
+   ret = -ENOMEM;
+   if (!prep->description)
+   goto error;
+   memcpy(prep->description, ctx.user_id, ulen);
+   prep->description[ulen] = ' ';
+   bin2hex(prep->description + ulen + 1,
+   ctx.raw_fingerprint + ctx.raw_fingerprint_len - 4, 4);
+   prep->description[ulen + 9] = 0;
+   pr_debug("desc '%s'\n", prep->description);
+   }
+
/* We're pinning the module by being linked against it */
__module_get(public_key_subtype.owner);
prep->payload.data[asym_subtype] = &public_key_subtype;
-- 
2.34.1




[PATCH v2 09/14] KEYS: Retry asym key search with partial ID in restrict_link_by_signature()

2024-08-18 Thread Roberto Sassu
From: Roberto Sassu 

Retry asymmetric key search in restrict_link_by_signature() to support the
case of partial IDs, provided by PGP signatures (only the last 8 bytes).

Although recently draft-ietf-openpgp-rfc4880bis-10 supports the signature
subpacket type 33, which contains the full issuer fingerprint, we cannot
rely on existing signatures to support it.

Signed-off-by: Roberto Sassu 
Signed-off-by: David Howells 
---
 crypto/asymmetric_keys/restrict.c | 10 --
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/crypto/asymmetric_keys/restrict.c 
b/crypto/asymmetric_keys/restrict.c
index afcd4d101ac5..dd3d4b3405f7 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -97,8 +97,14 @@ int restrict_link_by_signature(struct key *dest_keyring,
key = find_asymmetric_key(trust_keyring,
  sig->auth_ids[0], sig->auth_ids[1],
  sig->auth_ids[2], false);
-   if (IS_ERR(key))
-   return -ENOKEY;
+   if (IS_ERR(key)) {
+   /* Retry with a partial ID. */
+   key = find_asymmetric_key(trust_keyring,
+ sig->auth_ids[0], sig->auth_ids[1],
+ sig->auth_ids[2], true);
+   if (IS_ERR(key))
+   return -ENOKEY;
+   }
 
if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags))
ret = -ENOKEY;
-- 
2.34.1




[PATCH v2 08/14] KEYS: PGP-based public key signature verification

2024-08-18 Thread Roberto Sassu
From: David Howells 

Provide handlers for PGP-based public-key algorithm signature verification.
This does most of the work involved in signature verification as most of it
is public-key algorithm agnostic.  The public-key verification algorithm
itself is just the last little bit and is supplied the complete hash data
to process.

This requires glue logic putting on top to make use of it - something that
the patch introducing verify_pgp_signature() provides.

Signed-off-by: David Howells 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 MAINTAINERS|   1 +
 crypto/asymmetric_keys/Makefile|   3 +-
 crypto/asymmetric_keys/pgp_signature.c | 505 +
 include/crypto/pgp.h   |  30 ++
 4 files changed, 538 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/pgp_signature.c
 create mode 100644 include/crypto/pgp.h

diff --git a/MAINTAINERS b/MAINTAINERS
index f328373463b0..c005b59cc795 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3401,6 +3401,7 @@ L:keyri...@vger.kernel.org
 S: Maintained
 F: Documentation/crypto/asymmetric-keys.rst
 F: crypto/asymmetric_keys/
+F: include/crypto/pgp.h
 F: include/crypto/pkcs7.h
 F: include/crypto/public_key.h
 F: include/linux/verification.h
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index d197e8b23b83..e7ff01997eb2 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -87,4 +87,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
 
 obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o
 pgp_key_parser-y := \
-   pgp_public_key.o
+   pgp_public_key.o \
+   pgp_signature.o
diff --git a/crypto/asymmetric_keys/pgp_signature.c 
b/crypto/asymmetric_keys/pgp_signature.c
new file mode 100644
index ..9cd6d029d23a
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_signature.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* PGP public key signature verification [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGPSIG: "fmt
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pgp_parser.h"
+
+struct pgp_current_pkt {
+   enum pgp_packet_tag type;
+   u8 headerlen;
+   const u8 *data;
+   size_t datalen;
+};
+
+struct pgp_sig_verify {
+   enum pgp_signature_version sig_version : 8;
+   enum pgp_signature_type sig_type;
+   struct public_key_signature *sig;
+   u8 signed_hash_msw[2];
+   struct shash_desc *hash;
+   struct pgp_current_pkt pkt;
+};
+
+/*
+ * Find a key in the given keyring by issuer and authority.
+ */
+static struct key *pgp_request_asymmetric_key(struct pgp_sig_verify *ctx,
+ struct key *keyring)
+{
+   struct key *key;
+   __be32 *issuer32;
+   char id[20];
+
+   issuer32 = (__be32 *)ctx->sig->auth_ids[0]->data;
+   snprintf(id, sizeof(id), "id:%08x%08x", be32_to_cpu(issuer32[0]),
+be32_to_cpu(issuer32[1]));
+
+   kenter(",,%s", id);
+
+   pr_debug("Look up key: \"%s\"\n", id);
+
+   key = find_asymmetric_key(keyring, ctx->sig->auth_ids[0],
+ ctx->sig->auth_ids[1], NULL, true);
+   if (IS_ERR(key)) {
+   pr_debug("Request for public key '%08x%08x' err %ld\n",
+issuer32[0], issuer32[1], PTR_ERR(key));
+
+   switch (PTR_ERR(key)) {
+   /* Hide some search errors */
+   case -EACCES:
+   case -ENOTDIR:
+   case -EAGAIN:
+   kleave(" = -ENOKEY");
+   return ERR_PTR(-ENOKEY);
+   default:
+   kleave(" = %ld", PTR_ERR(key));
+   return ERR_CAST(key);
+   }
+   }
+
+   kleave(" = 0 [%x]", key_serial(key));
+   return key;
+}
+
+struct pgp_sig_parse_context {
+   struct pgp_parse_context pgp;
+   struct pgp_sig_parameters params;
+   struct pgp_current_pkt pkt;
+};
+
+static int pgp_parse_signature(struct pgp_parse_context *context,
+  enum pgp_packet_tag type,
+  u8 headerlen,
+  const u8 *data,
+  size_t datalen)
+{
+   struct pgp_sig_parse_context *ctx =
+   container_of(context, struct pgp_sig_parse_context, pgp);
+   struct pgp_sig_parameters tmp_params;
+   struct pgp_current_pkt tmp_pkt = { type, headerlen, data, datalen};
+   int ret;
+
+   ret = pgp_parse_sig_params(&data, &datalen, &tmp_params);
+   if (ret < 0)
+   return ret;
+
+   if (tmp_params.signature_type != PGP_SIG_BINARY_DOCU

[PATCH v2 10/14] KEYS: Calculate key digest and get signature of the key

2024-08-18 Thread Roberto Sassu
From: Roberto Sassu 

Calculate the digest of the signature, according to the RFC4880 section
5.2.4, get the last suitable signature with types 0x10 (Generic
certification of a User ID and Public-Key packet) or 0x13 (Positive
certification of a User ID and Public Key packet), and store it in the
asym_auth field of the key payload, so that it is available for validating
a restriction on a keyring.

Type 0x10 is included despite not giving the strongest trust guarantees,
since it is the one used by most common PGP implementations (including
gpg).

The rationale of taking the last signature is that, if there are multiple
signatures, that would be of a different issuer (not a self-signature),
that likely has more chances to be useful for the restriction verification.
If there is one (the self-signature), that will be used.

Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/pgp_public_key.c | 81 +
 1 file changed, 81 insertions(+)

diff --git a/crypto/asymmetric_keys/pgp_public_key.c 
b/crypto/asymmetric_keys/pgp_public_key.c
index e7f74d513a9e..91678cbdc196 100644
--- a/crypto/asymmetric_keys/pgp_public_key.c
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -14,6 +14,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "pgp_parser.h"
 
@@ -61,6 +62,8 @@ struct pgp_key_data_parse_context {
size_t raw_fingerprint_len;
const char *user_id;
size_t user_id_len;
+   const char *key_pkt;
+   size_t key_pkt_len;
 };
 
 /*
@@ -226,6 +229,12 @@ static int pgp_process_public_key(struct pgp_parse_context 
*context,
return -EBADMSG;
}
 
+   /* Pointer refers to data being processed. */
+   if (type == PGP_PKT_PUBLIC_KEY) {
+   ctx->key_pkt = data;
+   ctx->key_pkt_len = datalen;
+   }
+
pub = kzalloc(sizeof(*pub), GFP_KERNEL);
if (!pub)
return -ENOMEM;
@@ -316,6 +325,77 @@ pgp_key_generate_id(struct pgp_key_data_parse_context *ctx)
return NULL;
 }
 
+/*
+ * Calculate the digest of the signature according to the RFC4880, section
+ * 5.2.4 (packet type 0x13).
+ */
+static int pgp_key_add_sig_data(struct pgp_key_data_parse_context *ctx,
+   struct pgp_sig_verify *sig_ctx)
+{
+   loff_t offset = 0;
+   u8 *data;
+
+   if (!ctx->key_pkt_len || !ctx->user_id_len)
+   return 0;
+
+   /* 0x99 + key pkt len + key pkt + 0xb4 + user ID len + user ID */
+   data = kmalloc(1 + sizeof(u16) + ctx->key_pkt_len +
+  1 + sizeof(u32) + ctx->user_id_len, GFP_KERNEL);
+   if (!data)
+   return -ENOMEM;
+
+   data[offset++] = 0x99;
+   data[offset++] = ctx->key_pkt_len >> 8;
+   data[offset++] = ctx->key_pkt_len;
+
+   memcpy(data + offset, ctx->key_pkt, ctx->key_pkt_len);
+   offset += ctx->key_pkt_len;
+
+   if (pgp_sig_get_version(sig_ctx) == PGP_SIG_VERSION_4) {
+   data[offset++] = 0xb4;
+   data[offset++] = ctx->user_id_len >> 24;
+   data[offset++] = ctx->user_id_len >> 16;
+   data[offset++] = ctx->user_id_len >> 8;
+   data[offset++] = ctx->user_id_len;
+   }
+
+   memcpy(data + offset, ctx->user_id, ctx->user_id_len);
+   offset += ctx->user_id_len;
+
+   pgp_sig_add_data(sig_ctx, data, offset);
+   kfree(data);
+   return 0;
+}
+
+static struct public_key_signature *
+pgp_key_get_sig(struct key_preparsed_payload *prep,
+   struct pgp_key_data_parse_context *ctx)
+{
+   struct public_key_signature *sig = NULL;
+   struct pgp_sig_verify *sig_ctx;
+   bool keep_sig = false;
+   int ret;
+
+   sig_ctx = pgp_sig_parse(prep->data, prep->datalen);
+   if (IS_ERR(sig_ctx))
+   return NULL;
+
+   ret = pgp_key_add_sig_data(ctx, sig_ctx);
+   if (ret < 0)
+   goto out;
+
+   sig = pgp_sig_get_sig(sig_ctx, true);
+   if (IS_ERR(sig)) {
+   sig = NULL;
+   goto out;
+   }
+
+   keep_sig = true;
+out:
+   pgp_sig_verify_cancel(sig_ctx, keep_sig);
+   return sig;
+}
+
 /*
  * Attempt to parse the instantiation data blob for a key as a PGP packet
  * message holding a key.
@@ -380,6 +460,7 @@ static int pgp_key_parse(struct key_preparsed_payload *prep)
prep->payload.data[asym_subtype] = &public_key_subtype;
prep->payload.data[asym_key_ids] = pgp_key_generate_id(&ctx);
prep->payload.data[asym_crypto] = ctx.pub;
+   prep->payload.data[asym_auth] = pgp_key_get_sig(prep, &ctx);
prep->quotalen = 100;
return 0;
 
-- 
2.34.1




[PATCH v2 12/14] PGP: Provide a key type for testing PGP signatures

2024-08-18 Thread Roberto Sassu
From: David Howells 

Provide a key type for testing the PGP signature parser.  It is given a
non-detached PGP message as payload:

keyctl padd pgp_test a @s content.txt
gpg --compress-algo=none -s content.txt

Signed-off-by: David Howells 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/Kconfig|  13 +++
 crypto/asymmetric_keys/Makefile   |   2 +
 crypto/asymmetric_keys/pgp_library.c  |  64 +
 crypto/asymmetric_keys/pgp_test_key.c | 129 ++
 crypto/asymmetric_keys/pgplib.h   |  16 
 5 files changed, 224 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp_test_key.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 4c5c52cf54e4..d9c709e042f6 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -120,4 +120,17 @@ config PGP_KEY_PARSER
  for key data and provides the ability to instantiate a crypto key
  from a public key packet found inside the blob.
 
+config PGP_TEST_KEY
+   tristate "PGP testing key type"
+   depends on SYSTEM_DATA_VERIFICATION
+   depends on PGP_KEY_PARSER=y
+   help
+ This option provides a type of key that can be loaded up from a
+ PGP message - provided the message is signed by a trusted key.  If
+ it is, the PGP wrapper is discarded and reading the key returns
+ just the payload.  If it isn't, adding the key will fail with an
+ error.
+
+ This is intended for testing the PGP parser.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index e7ff01997eb2..507a78f9a0a1 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -89,3 +89,5 @@ obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o
 pgp_key_parser-y := \
pgp_public_key.o \
pgp_signature.o
+
+obj-$(CONFIG_PGP_TEST_KEY) += pgp_test_key.o
diff --git a/crypto/asymmetric_keys/pgp_library.c 
b/crypto/asymmetric_keys/pgp_library.c
index 2fd3e7d55f6f..2bcd5f46ad48 100644
--- a/crypto/asymmetric_keys/pgp_library.c
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -554,3 +554,67 @@ int pgp_parse_sig_params(const u8 **_data, size_t 
*_datalen,
return 0;
 }
 EXPORT_SYMBOL_GPL(pgp_parse_sig_params);
+
+#if IS_ENABLED(CONFIG_PGP_TEST_KEY)
+
+/**
+ * pgp_parse_literal_data - Parse basic params from a PGP literal data packet
+ * @data: Content of packet
+ * @datalen: Length of packet remaining
+ * @p: The basic parameters
+ *
+ * Parse the basic parameters from a PGP literal data packet [RFC 4880: 5.9]
+ * that are needed to work out what form the data is in and where it is.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
+int pgp_parse_literal_data(const u8 *data, size_t datalen,
+  struct pgp_literal_data_parameters *p)
+{
+   unsigned int tmp;
+
+   pr_devel("-->%s(,%zu,,)\n", __func__, datalen);
+
+   if (datalen < 6)
+   goto too_short;
+   datalen -= 6;
+
+   p->format = *data++;
+   switch (p->format) {
+   case PGP_LIT_FORMAT_BINARY:
+   case PGP_LIT_FORMAT_TEXT:
+   case PGP_LIT_FORMAT_TEXT_UTF8:
+   break;
+   default:
+   pr_debug("Literal data packet with unhandled format %02x\n",
+p->format);
+   return -EBADMSG;
+   }
+
+   p->filename_len = *data++;
+   p->filename_offset = 2;
+   if (datalen < p->filename_len)
+   goto too_short;
+   data += p->filename_len;
+   datalen -= p->filename_len;
+
+   tmp  = *data++ << 24;
+   tmp |= *data++ << 16;
+   tmp |= *data++ << 8;
+   tmp |= *data++;
+   p->time = tmp;
+
+   p->content_offset = 6 + p->filename_len;
+   p->content_len = datalen;
+
+   pr_devel("%x,%u,%x,%u\n",
+p->format, p->filename_len, p->time, p->content_len);
+   return 0;
+
+too_short:
+   pr_debug("Literal data packet too short\n");
+   return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_literal_data);
+
+#endif /* CONFIG_PGP_TEST_KEY */
diff --git a/crypto/asymmetric_keys/pgp_test_key.c 
b/crypto/asymmetric_keys/pgp_test_key.c
new file mode 100644
index ..e067dedf6ca0
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_test_key.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Testing module to load key from trusted PGP message
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGPtest: "fmt
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pgp_parser.h"
+
+MODULE_LICENSE("GPL");
+
+struct pgp_test_parse_context {
+   struct pgp_parse_context pgp;
+   struct pgp_literal_data_parameters params;
+   const void *content;
+};
+
+static int 

[PATCH v2 11/14] verification: introduce verify_pgp_signature()

2024-08-18 Thread Roberto Sassu
From: Roberto Sassu 

Introduce verify_pgp_signature() to verify PGP signatures from detached
data. It will be used by fsverity and by IMA.

Signed-off-by: Roberto Sassu 
---
 certs/system_keyring.c   | 70 
 include/linux/verification.h | 23 
 2 files changed, 93 insertions(+)

diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index 9de610bf1f4b..db0fde36a71b 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -16,6 +16,7 @@
 #include 
 #include 
 #include 
+#include 
 
 static struct key *builtin_trusted_keys;
 #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
@@ -418,6 +419,75 @@ int verify_pkcs7_signature(const void *data, size_t len,
 }
 EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
 
+#ifdef CONFIG_PGP_KEY_PARSER
+/**
+ * verify_pgp_signature - Verify a PGP-based signature on system data.
+ * @data: The data to be verified (must be provided).
+ * @len: Size of @data.
+ * @raw_pgp: The PGP message that is the signature.
+ * @pgp_len: The size of @raw_pgp.
+ * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
+ * (void *)1UL for all trusted keys).
+ * @usage: The use to which the key is being put.
+ * @view_content: Callback to gain access to content.
+ * @ctx: Context for callback.
+ */
+int verify_pgp_signature(const void *data, size_t len,
+const void *raw_pgp, size_t pgp_len,
+struct key *trusted_keys,
+enum key_being_used_for usage,
+int (*view_content)(void *ctx,
+const void *data, size_t len,
+size_t asn1hdrlen),
+void *ctx)
+{
+   struct pgp_sig_verify *pgp_ctx;
+   int ret;
+
+   if (!data || !len)
+   return -EINVAL;
+
+   pgp_ctx = pgp_sig_parse(raw_pgp, pgp_len);
+   if (IS_ERR(pgp_ctx))
+   return PTR_ERR(pgp_ctx);
+
+   if (!trusted_keys) {
+   trusted_keys = builtin_trusted_keys;
+   } else if (trusted_keys == VERIFY_USE_SECONDARY_KEYRING) {
+#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
+   trusted_keys = secondary_trusted_keys;
+#else
+   trusted_keys = builtin_trusted_keys;
+#endif
+   } else if (trusted_keys == VERIFY_USE_PLATFORM_KEYRING) {
+#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING
+   trusted_keys = platform_trusted_keys;
+#else
+   trusted_keys = NULL;
+#endif
+   if (!trusted_keys) {
+   ret = -ENOKEY;
+   pr_devel("PGP platform keyring is not available\n");
+   goto error;
+   }
+   }
+
+   /* The data should be detached - so we need to supply it. */
+   if (pgp_sig_add_data(pgp_ctx, data, len)) {
+   pr_err("Failed to supply data for PGP signature\n");
+   ret = -EBADMSG;
+   goto error;
+   }
+
+   ret = pgp_sig_verify(pgp_ctx, trusted_keys);
+error:
+   pgp_sig_verify_cancel(pgp_ctx, false);
+   pr_devel("<==%s() = %d\n", __func__, ret);
+   return ret;
+}
+EXPORT_SYMBOL_GPL(verify_pgp_signature);
+
+#endif /* CONFIG_PGP_KEY_PARSER */
 #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
 
 #ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING
diff --git a/include/linux/verification.h b/include/linux/verification.h
index cb2d47f28091..e1a3b9424b67 100644
--- a/include/linux/verification.h
+++ b/include/linux/verification.h
@@ -63,6 +63,29 @@ extern int verify_pkcs7_message_sig(const void *data, size_t 
len,
size_t asn1hdrlen),
void *ctx);
 
+#ifdef CONFIG_PGP_KEY_PARSER
+extern int verify_pgp_signature(const void *data, size_t len,
+   const void *raw_pgp, size_t pgp_len,
+   struct key *trusted_keys,
+   enum key_being_used_for usage,
+   int (*view_content)(void *ctx,
+   const void *data, size_t len,
+   size_t asn1hdrlen),
+   void *ctx);
+#else
+static inline int verify_pgp_signature(const void *data, size_t len,
+   const void *raw_pgp, size_t pgp_len,
+   struct key *trusted_keys,
+   enum key_being_used_for usage,
+   int (*view_content)(void *ctx,
+   const void *data, size_t len,
+   size_t asn1hdrlen),
+   void *ctx)
+{
+   return -EOPNOTSUPP;
+}
+#endif /* CONFIG_PGP_KEY_PARSER */
+
 #ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
 extern int verify_pefile_signature(c

[PATCH v2 13/14] KEYS: Provide a function to load keys from a PGP keyring blob

2024-08-18 Thread Roberto Sassu
From: David Howells 

Provide a function to load keys from a PGP keyring blob to the built-in
keyring:

int preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
 struct key *keyring);

Descriptions are generated from user ID notes and key fingerprints.  The
keys will actually be identified by the ID calculated from the PGP data
rather than by the description, so this shouldn't be a problem.

The keys are attached to the keyring supplied.

Looking as root in /proc/keys after the built-in keyring has been loaded:

383a00c1 I-- 1 perm 1f03 0 0 asymmetri \
Red Hat, Inc. dbeca166: PGP.DSA dbeca166 []

Thanks to Tetsuo Handa  for some
pointing out some errors.

Signed-off-by: David Howells 
Co-developed-by: Roberto Sassu 
Signed-off-by: Roberto Sassu 
---
 crypto/asymmetric_keys/Kconfig   |   8 ++
 crypto/asymmetric_keys/Makefile  |   1 +
 crypto/asymmetric_keys/pgp_preload.c | 111 +++
 include/crypto/pgp.h |   8 +-
 4 files changed, 127 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/pgp_preload.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index d9c709e042f6..6379efded6f4 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -133,4 +133,12 @@ config PGP_TEST_KEY
 
  This is intended for testing the PGP parser.
 
+config PGP_PRELOAD
+   bool "PGP public key preloading facility"
+   depends on SYSTEM_TRUSTED_KEYRING
+   select PGP_KEY_PARSER
+   help
+ This option provides a facility for the kernel to preload PGP-wrapped
+ bundles of keys during boot to the built-in keyring.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 507a78f9a0a1..2fa55a7830e6 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -84,6 +84,7 @@ $(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c 
$(obj)/mscode.asn1.h
 # PGP handling
 #
 obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
+obj-$(CONFIG_PGP_PRELOAD) += pgp_preload.o
 
 obj-$(CONFIG_PGP_KEY_PARSER) += pgp_key_parser.o
 pgp_key_parser-y := \
diff --git a/crypto/asymmetric_keys/pgp_preload.c 
b/crypto/asymmetric_keys/pgp_preload.c
new file mode 100644
index ..e0776dc8f928
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_preload.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Asymmetric key request handling
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "pgp_parser.h"
+
+struct preload_pgp_keys_context {
+   struct pgp_parse_context pgp;
+   key_ref_t keyring;
+   const u8 *key_start;
+   const u8 *key_end;
+   bool found_key;
+};
+
+/*
+ * Create a key.
+ */
+static void __init create_pgp_key(struct preload_pgp_keys_context *ctx)
+{
+   key_ref_t key;
+
+   key = key_create_or_update(ctx->keyring,
+  "asymmetric",
+  NULL,
+  ctx->key_start,
+  ctx->key_end - ctx->key_start,
+  ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
+   KEY_USR_VIEW | KEY_USR_READ),
+  KEY_ALLOC_NOT_IN_QUOTA |
+  KEY_ALLOC_BUILT_IN |
+  KEY_ALLOC_BYPASS_RESTRICTION);
+   if (IS_ERR(key)) {
+   pr_notice("Ignoring PGP key, error: %ld\n", PTR_ERR(key));
+   return;
+   }
+
+   pr_notice("Loaded PGP key '%s'\n",
+ key_ref_to_ptr(key)->description);
+
+   key_ref_put(key);
+}
+
+/*
+ * Extract a public key or subkey from the PGP stream.
+ */
+static int __init found_pgp_key(struct pgp_parse_context *context,
+   enum pgp_packet_tag type, u8 headerlen,
+   const u8 *data, size_t datalen)
+{
+   struct preload_pgp_keys_context *ctx =
+   container_of(context, struct preload_pgp_keys_context, pgp);
+
+   if (ctx->found_key) {
+   ctx->key_end = data - headerlen;
+   create_pgp_key(ctx);
+   }
+
+   ctx->key_start = data - headerlen;
+   ctx->found_key = true;
+   return 0;
+}
+
+/**
+ * preload_pgp_keys - Load keys from a PGP keyring blob
+ * @pgpdata: The PGP keyring blob containing the keys.
+ * @pgpdatalen: The size of the @pgpdata blob.
+ * @keyring: The keyring to add the new keys to.
+ *
+ * Preload a pack of keys from a PGP keyring blob.
+ *
+ * The keys have their descriptions generated from the user ID and fingerprint
+ * in the PGP stream.  Since keys can be matched on their key IDs independently
+ * of the key description, the desc

[PATCH v2 14/14] KEYS: Introduce load_pgp_public_keyring()

2024-08-18 Thread Roberto Sassu
From: Roberto Sassu 

Preload PGP keys from 'pubring.gpg', placed in certs/ of the kernel source
directory.

Signed-off-by: Roberto Sassu 
---
 certs/Kconfig   | 11 +++
 certs/Makefile  |  7 +++
 certs/system_certificates.S | 18 ++
 certs/system_keyring.c  | 23 +++
 4 files changed, 59 insertions(+)

diff --git a/certs/Kconfig b/certs/Kconfig
index 78307dc25559..9b7ece1e45fa 100644
--- a/certs/Kconfig
+++ b/certs/Kconfig
@@ -154,4 +154,15 @@ config SYSTEM_BLACKLIST_AUTH_UPDATE
  keyring.  The PKCS#7 signature of the description is set in the key
  payload.  Blacklist keys cannot be removed.
 
+config PGP_PRELOAD_PUBLIC_KEYS
+   bool "Preload PGP public keys"
+   depends on SYSTEM_TRUSTED_KEYRING
+   select PGP_PRELOAD
+   default n
+   help
+ Load at boot time the PGP public keys from a reserved area (populated
+ with the content of 'certs/pubring.gpg' provided at kernel build
+ time), and add them to the built-in keyring. Invalid keys are ignored
+ and the loading continues.
+
 endmenu
diff --git a/certs/Makefile b/certs/Makefile
index 1094e3860c2a..7a3d68441e09 100644
--- a/certs/Makefile
+++ b/certs/Makefile
@@ -31,6 +31,13 @@ $(obj)/system_certificates.o: $(obj)/x509_certificate_list
 $(obj)/x509_certificate_list: $(CONFIG_SYSTEM_TRUSTED_KEYS) 
$(obj)/extract-cert FORCE
$(call if_changed,extract_certs)
 
+ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS
+ifeq ($(shell ls $(srctree)/certs/pubring.gpg 2> /dev/null), 
$(srctree)/certs/pubring.gpg)
+AFLAGS_system_certificates.o := -DHAVE_PUBRING_GPG
+$(obj)/system_certificates.o: $(srctree)/certs/pubring.gpg
+endif
+endif
+
 targets += x509_certificate_list
 
 # If module signing is requested, say by allyesconfig, but a key has not been
diff --git a/certs/system_certificates.S b/certs/system_certificates.S
index 003e25d4a17e..b3cbf0811e3f 100644
--- a/certs/system_certificates.S
+++ b/certs/system_certificates.S
@@ -44,3 +44,21 @@ module_cert_size:
 #else
.long __module_cert_end - __module_cert_start
 #endif
+
+   .align 8
+   .globl pgp_public_keys
+pgp_public_keys:
+__pgp_key_list_start:
+#ifdef HAVE_PUBRING_GPG
+   .incbin "certs/pubring.gpg"
+#endif
+__pgp_key_list_end:
+
+   .align 8
+   .globl pgp_public_keys_size
+pgp_public_keys_size:
+#ifdef CONFIG_64BIT
+   .quad __pgp_key_list_end - __pgp_key_list_start
+#else
+   .long __pgp_key_list_end - __pgp_key_list_start
+#endif
diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index db0fde36a71b..7b4c1dfa7fb3 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -32,6 +32,10 @@ static struct key *platform_trusted_keys;
 extern __initconst const u8 system_certificate_list[];
 extern __initconst const unsigned long system_certificate_list_size;
 extern __initconst const unsigned long module_cert_size;
+#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS
+extern __initconst const u8 pgp_public_keys[];
+extern __initconst const unsigned long pgp_public_keys_size;
+#endif
 
 /**
  * restrict_link_by_builtin_trusted - Restrict keyring addition by built-in CA
@@ -268,6 +272,15 @@ __init int load_module_cert(struct key *keyring)
if (!IS_ENABLED(CONFIG_IMA_APPRAISE_MODSIG))
return 0;
 
+#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS
+   pr_notice("Load PGP public keys to keyring %s\n", keyring->description);
+
+   if (preload_pgp_keys(pgp_public_keys,
+pgp_public_keys_size,
+keyring) < 0)
+   pr_err("Can't load PGP public keys\n");
+#endif
+
pr_notice("Loading compiled-in module X.509 certificates\n");
 
return x509_load_certificate_list(system_certificate_list,
@@ -292,6 +305,16 @@ static __init int load_system_certificate_list(void)
size = system_certificate_list_size - module_cert_size;
 #endif
 
+#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS
+   pr_notice("Load PGP public keys to keyring %s\n",
+ builtin_trusted_keys->description);
+
+   if (preload_pgp_keys(pgp_public_keys,
+pgp_public_keys_size,
+builtin_trusted_keys) < 0)
+   pr_err("Can't load PGP public keys\n");
+#endif
+
return x509_load_certificate_list(p, size, builtin_trusted_keys);
 }
 late_initcall(load_system_certificate_list);
-- 
2.34.1