linux-integrity.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
@ 2024-09-11 12:28 Roberto Sassu
  2024-09-11 12:28 ` [PATCH v3 01/14] mpi: Introduce mpi_key_length() Roberto Sassu
                   ` (14 more replies)
  0 siblings, 15 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:28 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

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, there is renewed interest in supporting PGP keys and signatures for
the following use cases:

- EFI variables in modern Lenovo laptops are in PGP form:
  https://lore.kernel.org/keyrings/2493611.1723748374@warthog.procyon.org.uk/

- Appraisal of RPM package headers for integrity check
  https://lore.kernel.org/linux-integrity/20240905152512.3781098-1-roberto.sassu@huaweicloud.com/


To make these use cases possible, introduce support for PGP keys and
signatures in the kernel, and load provided PGP keys (e.g. Linux
distribution own PGP keys) in the built-in keyring and .ima keyrings.

This feature does not interfere with existing signature verification
mechanisms, such as the one for kernel modules. One has to explicitly call
verify_pgp_signature() to take advantage of it.

For the envisioned use cases, PGP operations cannot be done in user space,
since the consumers are in the kernel itself (Integrity Digest Cache and
IMA). Also they cannot be done in a trusted initial ram disk, since PGP
operations can occur also while the system is running (e.g. after software
package installation).

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).
However, 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. 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

v2 [3]:
- Fix description of verify_pgp_signature()
- Change references from RFC 4880 to RFC 9580 (suggested by Jonathan
  McDowell)
- Remove support for v2 and v3 PGP keys (suggested by Jonathan McDowell)
- Explain better CONFIG_PGP_TEST_KEY
- Add MODULE_DESCRIPTION() for all kernel modules (suggested by Jeff
  Johnson)
- Don't fill capabilities and MPIs for unsupported key algorithms
- Check if there is enough data when parsing PGP key MPIs and RSA RAW keys
- Fix style issues
- Fix debug messages in pgp_request_asymmetric_key()
- Search verification key in the secondary keyring for the pgp_test key
  type

v1 [2]:
- 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 <crypto/pgp.h> 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 [1]:
- 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://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-modsign.git/log/?h=pgp-parser
[2] https://lore.kernel.org/linux-integrity/20220111180318.591029-1-roberto.sassu@huawei.com/
[3] https://lore.kernel.org/linux-integrity/20240818165756.629203-1-roberto.sassu@huaweicloud.com/

David Howells (8):
  PGPLIB: PGP definitions (RFC 9580)
  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 key search with partial ID in
    restrict_link_by_signature()
  KEYS: Calculate key digest and get signature of the key
  verification: introduce verify_pgp_signature()
  KEYS: Introduce load_pgp_public_keyring()

 MAINTAINERS                             |   1 +
 certs/Kconfig                           |  11 +
 certs/Makefile                          |   7 +
 certs/system_certificates.S             |  18 +
 certs/system_keyring.c                  |  94 ++++
 crypto/asymmetric_keys/Kconfig          |  39 ++
 crypto/asymmetric_keys/Makefile         |  13 +
 crypto/asymmetric_keys/pgp.h            | 216 +++++++++
 crypto/asymmetric_keys/pgp_library.c    | 610 ++++++++++++++++++++++++
 crypto/asymmetric_keys/pgp_parser.h     |  18 +
 crypto/asymmetric_keys/pgp_preload.c    | 111 +++++
 crypto/asymmetric_keys/pgp_public_key.c | 482 +++++++++++++++++++
 crypto/asymmetric_keys/pgp_signature.c  | 510 ++++++++++++++++++++
 crypto/asymmetric_keys/pgp_test_key.c   | 131 +++++
 crypto/asymmetric_keys/pgplib.h         |  74 +++
 crypto/asymmetric_keys/restrict.c       |  10 +-
 crypto/rsa.c                            |  14 +-
 crypto/rsa_helper.c                     |  83 +++-
 include/crypto/internal/rsa.h           |   6 +
 include/crypto/pgp.h                    |  36 ++
 include/linux/mpi.h                     |   2 +
 include/linux/verification.h            |  23 +
 lib/crypto/mpi/mpicoder.c               |  33 +-
 23 files changed, 2527 insertions(+), 15 deletions(-)
 create mode 100644 crypto/asymmetric_keys/pgp.h
 create mode 100644 crypto/asymmetric_keys/pgp_library.c
 create mode 100644 crypto/asymmetric_keys/pgp_parser.h
 create mode 100644 crypto/asymmetric_keys/pgp_preload.c
 create mode 100644 crypto/asymmetric_keys/pgp_public_key.c
 create mode 100644 crypto/asymmetric_keys/pgp_signature.c
 create mode 100644 crypto/asymmetric_keys/pgp_test_key.c
 create mode 100644 crypto/asymmetric_keys/pgplib.h
 create mode 100644 include/crypto/pgp.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH v3 01/14] mpi: Introduce mpi_key_length()
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
@ 2024-09-11 12:28 ` Roberto Sassu
  2024-09-12 13:26   ` Jarkko Sakkinen
  2024-09-11 12:28 ` [PATCH v3 02/14] rsa: add parser of raw format Roberto Sassu
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:28 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

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

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---
 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


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 02/14] rsa: add parser of raw format
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
  2024-09-11 12:28 ` [PATCH v3 01/14] mpi: Introduce mpi_key_length() Roberto Sassu
@ 2024-09-11 12:28 ` Roberto Sassu
  2024-09-12 13:33   ` Jarkko Sakkinen
  2024-09-11 12:29 ` [PATCH v3 03/14] PGPLIB: PGP definitions (RFC 9580) Roberto Sassu
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:28 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

Parse the RSA key with RAW format if the ASN.1 parser returns an error, to
avoid passing somehow the key format as parameter.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---
 crypto/rsa.c                  | 14 ++++--
 crypto/rsa_helper.c           | 83 ++++++++++++++++++++++++++++++++++-
 include/crypto/internal/rsa.h |  6 +++
 3 files changed, 97 insertions(+), 6 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..40a17ebc972f 100644
--- a/crypto/rsa_helper.c
+++ b/crypto/rsa_helper.c
@@ -9,6 +9,7 @@
 #include <linux/export.h>
 #include <linux/err.h>
 #include <linux/fips.h>
+#include <linux/mpi.h>
 #include <crypto/internal/rsa.h>
 #include "rsapubkey.asn1.h"
 #include "rsaprivkey.asn1.h"
@@ -148,6 +149,42 @@ 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++) {
+		if (key_len < 2)
+			return -EINVAL;
+
+		ret = mpi_key_length(key_ptr, len, NULL, &nbytes);
+		if (ret < 0)
+			return ret;
+
+		key_ptr += 2;
+		key_len -= 2;
+
+		if (key_len < nbytes)
+			return -EINVAL;
+
+		ret = func[i](rsa_key, 0, 0, key_ptr, nbytes);
+		if (ret < 0)
+			return ret;
+
+		key_ptr += nbytes;
+		key_len -= nbytes;
+	}
+
+	return 0;
+}
+
 /**
  * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the
  *                       provided struct rsa_key, pointers to the raw key as is,
@@ -157,7 +194,7 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag,
  * @key:	key in BER format
  * @key_len:	length of key
  *
- * Return:	0 on success or error code in case of error
+ * Return:	0 on success or error code in case of error.
  */
 int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
 		      unsigned int key_len)
@@ -166,6 +203,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
@@ -176,7 +234,7 @@ EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
  * @key:	key in BER format
  * @key_len:	length of key
  *
- * Return:	0 on success or error code in case of error
+ * Return:	0 on success or error code in case of error.
  */
 int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
 		       unsigned int key_len)
@@ -184,3 +242,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..7141e806ceea 100644
--- a/include/crypto/internal/rsa.h
+++ b/include/crypto/internal/rsa.h
@@ -50,8 +50,14 @@ struct rsa_key {
 int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
 		      unsigned int key_len);
 
+int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key,
+			  unsigned int key_len);
+
 int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
 		       unsigned int key_len);
 
+int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key,
+			   unsigned int key_len);
+
 extern struct crypto_template rsa_pkcs1pad_tmpl;
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 03/14] PGPLIB: PGP definitions (RFC 9580)
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
  2024-09-11 12:28 ` [PATCH v3 01/14] mpi: Introduce mpi_key_length() Roberto Sassu
  2024-09-11 12:28 ` [PATCH v3 02/14] rsa: add parser of raw format Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-12 13:54   ` Jarkko Sakkinen
  2024-09-11 12:29 ` [PATCH v3 04/14] PGPLIB: Basic packet parser Roberto Sassu
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

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

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/pgp.h | 216 +++++++++++++++++++++++++++++++++++
 1 file changed, 216 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 000000000000..eaf0ab8e0373
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp.h
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* PGP definitions (RFC 9580)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/types.h>
+
+struct pgp_key_ID {
+	u8 id[8];
+} __packed;
+
+struct pgp_time {
+	u8 time[4];
+} __packed;
+
+/*
+ * PGP public-key algorithm identifiers [RFC 9580: 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_ECDH			= 18,
+	PGP_PUBKEY_ECDSA		= 19,
+	PGP_PUBKEY_EDDSA_LEGACY		= 22,
+	PGP_PUBKEY_X25519		= 25,
+	PGP_PUBKEY_X448			= 26,
+	PGP_PUBKEY_ED25519		= 27,
+	PGP_PUBKEY_ED448		= 28,
+	PGP_PUBKEY__LAST
+};
+
+/*
+ * PGP symmetric-key algorithm identifiers [RFC 9580: 9.3]
+ */
+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_SYMKEY_CAMELIA_128KEY	= 11,
+	PGP_SYMKEY_CAMELIA_192KEY	= 12,
+	PGP_SYMKEY_CAMELIA_256KEY	= 13,
+	PGP_SYMKEY__LAST
+};
+
+/*
+ * PGP compression algorithm identifiers [RFC 9580: 9.4]
+ */
+enum pgp_compr_algo {
+	PGP_COMPR_UNCOMPRESSED		= 0,
+	PGP_COMPR_ZIP			= 1,
+	PGP_COMPR_ZLIB			= 2,
+	PGP_COMPR_BZIP2			= 3,
+	PGP_COMPR__LAST
+};
+
+/*
+ * PGP hash algorithm identifiers [RFC 9580: 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_SHA3_256		= 12,
+	PGP_HASH_SHA3_512		= 14,
+	PGP_HASH__LAST
+};
+
+extern const char *const pgp_hash_algorithms[PGP_HASH__LAST];
+
+/*
+ * PGP packet type tags [RFC 9580: 5].
+ */
+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 [RFC 9580: 5.2].
+ */
+enum pgp_signature_version {
+	PGP_SIG_VERSION_3			= 3,
+	PGP_SIG_VERSION_4			= 4,
+};
+
+/*
+ * Signature types [RFC 9580: 5.2.1].
+ */
+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 issuer;
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	enum pgp_hash_algo hash_algo : 8;
+} __packed;
+
+struct pgp_signature_v4_packet {
+	enum pgp_signature_version version : 8;	/* == PGP_SIG_VERSION_4 */
+	enum pgp_signature_type signature_type : 8;
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	enum pgp_hash_algo hash_algo : 8;
+} __packed;
+
+/*
+ * V4 signature subpacket types [RFC 9580: 5.2.3.7].
+ */
+enum pgp_sig_subpkt_type {
+	PGP_SIG_CREATION_TIME			= 2,
+	PGP_SIG_EXPIRATION_TIME			= 3,
+	PGP_SIG_EXPORTABLE_CERT			= 4,
+	PGP_SIG_TRUST_SIG			= 5,
+	PGP_SIG_REGEXP				= 6,
+	PGP_SIG_REVOCABLE			= 7,
+	PGP_SIG_KEY_EXPIRATION_TIME		= 9,
+	PGP_SIG_PREF_SYM_ALGO			= 11,
+	PGP_SIG_REVOCATION_KEY			= 12,
+	PGP_SIG_ISSUER				= 16,
+	PGP_SIG_NOTATION_DATA			= 20,
+	PGP_SIG_PREF_HASH_ALGO			= 21,
+	PGP_SIG_PREF_COMPR_ALGO			= 22,
+	PGP_SIG_KEY_SERVER_PREFS		= 23,
+	PGP_SIG_PREF_KEY_SERVER			= 24,
+	PGP_SIG_PRIMARY_USER_ID			= 25,
+	PGP_SIG_POLICY_URI			= 26,
+	PGP_SIG_KEY_FLAGS			= 27,
+	PGP_SIG_SIGNERS_USER_ID			= 28,
+	PGP_SIG_REASON_FOR_REVOCATION		= 29,
+	PGP_SIG_FEATURES			= 30,
+	PGP_SIG_TARGET				= 31,
+	PGP_SIG_EMBEDDED_SIG			= 32,
+	PGP_SIG_ISSUER_FINGERPRINT		= 33,
+	PGP_SIG_INTENDED_RECIPIENT_FINGERPRINT	= 35,
+	PGP_SIG_PREFERRED_AEAD_CIPHERS		= 39,
+	PGP_SIG__LAST
+};
+
+#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK	0x80
+
+/*
+ * Key (tag 5, 6, 7 and 14) packet
+ */
+enum pgp_key_version {
+	PGP_KEY_VERSION_4			= 4,
+};
+
+struct pgp_key_v4_packet {
+	enum pgp_key_version version : 8;
+	struct pgp_time	creation_time;
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	u8 key_material[];
+} __packed;
+
+/*
+ * Literal Data (tag 11) packet
+ */
+enum pgp_literal_data_format {
+	PGP_LIT_FORMAT_BINARY			= 0x62,
+	PGP_LIT_FORMAT_TEXT			= 0x74,
+	PGP_LIT_FORMAT_TEXT_UTF8		= 0x75,
+};
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 04/14] PGPLIB: Basic packet parser
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (2 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 03/14] PGPLIB: PGP definitions (RFC 9580) Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-12 13:57   ` Jarkko Sakkinen
  2024-09-11 12:29 ` [PATCH v3 05/14] PGPLIB: Signature parser Roberto Sassu
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

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 <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig       |   6 +
 crypto/asymmetric_keys/Makefile      |   5 +
 crypto/asymmetric_keys/pgp_library.c | 262 +++++++++++++++++++++++++++
 crypto/asymmetric_keys/pgplib.h      |  33 ++++
 4 files changed, 306 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..8215e3fcd8db 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 9580) 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 000000000000..1b87f8af411b
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0
+/* PGP packet parser (RFC 9580)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGPL: "fmt
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "pgplib.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PGP library");
+
+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",
+	[PGP_HASH_SHA3_256]		= "sha3-256",
+	[PGP_HASH_SHA3_512]		= "sha3-512",
+};
+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 9580: 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 ... 0xdf:
+			/* Two-byte length */
+			if (datalen < 2)
+				goto short_packet;
+			size = (data[0] - 192) * 256;
+			size += data[1] + 192;
+			data += 2;
+			datalen -= 2;
+			*_headerlen = 3;
+			break;
+		case 0xff:
+			/* Five-byte length */
+			if (datalen < 5)
+				goto short_packet;
+			size =  data[1] << 24;
+			size |= data[2] << 16;
+			size |= data[3] << 8;
+			size |= data[4];
+			data += 5;
+			datalen -= 5;
+			*_headerlen = 6;
+			break;
+		default:
+			pr_debug("Partial body length packet not supported\n");
+			return -EBADMSG;
+		}
+	} else {
+		/* Old packet length format */
+		u8 length_type = type & 0x03;
+
+		type >>= 2;
+		pr_devel("old format: t=%u lt=%u\n", type, length_type);
+
+		switch (length_type) {
+		case 0:
+			/* One-byte length */
+			size = data[0];
+			data++;
+			datalen--;
+			*_headerlen = 2;
+			break;
+		case 1:
+			/* Two-byte length */
+			if (datalen < 2)
+				goto short_packet;
+			size  = data[0] << 8;
+			size |= data[1];
+			data += 2;
+			datalen -= 2;
+			*_headerlen = 3;
+			break;
+		case 2:
+			/* Four-byte length */
+			if (datalen < 4)
+				goto short_packet;
+			size  = data[0] << 24;
+			size |= data[1] << 16;
+			size |= data[2] << 8;
+			size |= data[3];
+			data += 4;
+			datalen -= 4;
+			*_headerlen = 5;
+			break;
+		default:
+			pr_debug("Indefinite length packet not supported\n");
+			return -EBADMSG;
+		}
+	}
+
+	pr_devel("datalen=%zu size=%zu\n", datalen, size);
+	if (datalen < size)
+		goto short_packet;
+	if (size > INT_MAX)
+		goto too_big;
+
+	*_data = data;
+	*_datalen = datalen;
+	*_type = type;
+	pr_devel("Found packet type=%u size=%zd\n", type, size);
+	return size;
+
+short_packet:
+	pr_debug("Attempt to parse short packet\n");
+	return -EBADMSG;
+too_big:
+	pr_debug("Signature subpacket size >2G\n");
+	return -EMSGSIZE;
+}
+
+/**
+ * pgp_parse_packets - Parse a set of PGP packets
+ * @data: Data to be parsed (updated)
+ * @datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP packets [RFC 9580: 4].
+ *
+ * Return: 0 on successful parsing, a negative value otherwise.
+ */
+int pgp_parse_packets(const u8 *data, size_t datalen,
+		      struct pgp_parse_context *ctx)
+{
+	enum pgp_packet_tag type;
+	ssize_t pktlen;
+	u8 headerlen;
+	int ret;
+
+	while (datalen > 2) {
+		pktlen = pgp_parse_packet_header(&data, &datalen, &type,
+						 &headerlen);
+		if (pktlen < 0)
+			return pktlen;
+
+		if ((ctx->types_of_interest >> type) & 1) {
+			ret = ctx->process_packet(ctx, type, headerlen,
+						  data, pktlen);
+			if (ret < 0)
+				return ret;
+		}
+		data += pktlen;
+		datalen -= pktlen;
+	}
+
+	if (datalen != 0) {
+		pr_debug("Excess octets in packet stream\n");
+		return -EBADMSG;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_packets);
+
+/**
+ * pgp_parse_public_key - Parse the common part of a PGP pubkey packet
+ * @_data: Content of packet (updated)
+ * @_datalen: Length of packet remaining (updated)
+ * @pk: Public key data
+ *
+ * Parse the common data struct for a PGP pubkey packet [RFC 9580: 5.5.2].
+ *
+ * Return: 0 on successful parsing, a negative value otherwise.
+ */
+int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+			 struct pgp_parse_pubkey *pk)
+{
+	const u8 *data = *_data;
+	size_t datalen = *_datalen;
+	unsigned int tmp;
+
+	if (datalen < 12) {
+		pr_debug("Public key packet too short\n");
+		return -EBADMSG;
+	}
+
+	pk->version = *data++;
+	switch (pk->version) {
+	case PGP_KEY_VERSION_4:
+		break;
+	default:
+		pr_debug("Public key packet with unhandled version %d\n",
+			 pk->version);
+		return -EBADMSG;
+	}
+
+	tmp  = *data++ << 24;
+	tmp |= *data++ << 16;
+	tmp |= *data++ << 8;
+	tmp |= *data++;
+	pk->creation_time = tmp;
+	if (pk->version == PGP_KEY_VERSION_4)
+		pk->expires_at = 0; /* Have to get it from the selfsignature */
+
+	pk->pubkey_algo = *data++;
+	datalen -= 6;
+
+	pr_devel("%x,%x,%lx,%lx\n",
+		 pk->version, pk->pubkey_algo, pk->creation_time,
+		 pk->expires_at);
+
+	*_data = data;
+	*_datalen = datalen;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_public_key);
diff --git a/crypto/asymmetric_keys/pgplib.h b/crypto/asymmetric_keys/pgplib.h
new file mode 100644
index 000000000000..3ec4b408a11e
--- /dev/null
+++ b/crypto/asymmetric_keys/pgplib.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* PGP library definitions (RFC 9580)
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include "pgp.h"
+
+/*
+ * PGP library packet parser
+ */
+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);
+};
+
+extern int pgp_parse_packets(const u8 *data, size_t datalen,
+			     struct pgp_parse_context *ctx);
+
+struct pgp_parse_pubkey {
+	enum pgp_key_version version : 8;
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	__kernel_old_time_t creation_time;
+	__kernel_old_time_t expires_at;
+};
+
+extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+				struct pgp_parse_pubkey *pk);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 05/14] PGPLIB: Signature parser
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (3 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 04/14] PGPLIB: Basic packet parser Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-12 13:58   ` Jarkko Sakkinen
  2024-09-11 12:29 ` [PATCH v3 06/14] KEYS: PGP data parser Roberto Sassu
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

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 <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 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 1b87f8af411b..a9708ccbcb81 100644
--- a/crypto/asymmetric_keys/pgp_library.c
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -260,3 +260,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 9580: 5.2.3.7].
+ *
+ * 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 9580: 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 -= pktlen;
+	}
+
+	if (datalen != 0) {
+		pr_debug("Excess octets in signature subpacket stream\n");
+		return -EBADMSG;
+	}
+
+	return 0;
+}
+
+struct pgp_parse_sig_params_ctx {
+	struct pgp_parse_sig_context base;
+	struct pgp_sig_parameters *params;
+	bool got_the_issuer;
+};
+
+/*
+ * Process a V4 signature subpacket.
+ */
+static int pgp_process_sig_params_subpkt(struct pgp_parse_sig_context *context,
+					 enum pgp_sig_subpkt_type type,
+					 const u8 *data,
+					 size_t datalen)
+{
+	struct pgp_parse_sig_params_ctx *ctx =
+		container_of(context, struct pgp_parse_sig_params_ctx, base);
+
+	if (ctx->got_the_issuer) {
+		pr_debug("V4 signature packet has multiple issuers\n");
+		return -EBADMSG;
+	}
+
+	if (datalen != 8) {
+		pr_debug("V4 signature issuer subpkt not 8 long (%zu)\n",
+			 datalen);
+		return -EBADMSG;
+	}
+
+	memcpy(&ctx->params->issuer, data, 8);
+	ctx->got_the_issuer = true;
+	return 0;
+}
+
+/**
+ * pgp_parse_sig_params - Parse basic parameters from a PGP signature packet
+ * @_data: Content of packet (updated)
+ * @_datalen: Length of packet remaining (updated)
+ * @p: The basic parameters
+ *
+ * Parse the basic parameters from a PGP signature packet [RFC 9580: 5.2] that
+ * are needed to start off a signature verification operation.  The only ones
+ * actually necessary are the signature type (which affects how the data is
+ * transformed) and the hash algorithm.
+ *
+ * We also extract the public key algorithm and the issuer's key ID as we'll
+ * need those to determine if we actually have the public key available.  If
+ * not, then we can't verify the signature anyway.
+ *
+ * Return: 0 if successful or a negative error code.  *_data and *_datalen are
+ * updated to point to the 16-bit subset of the hash value and the set of MPIs.
+ */
+int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
+			 struct pgp_sig_parameters *p)
+{
+	const u8 *data = *_data;
+	size_t datalen = *_datalen;
+	int ret;
+
+	pr_devel("-->%s(,%zu,,)\n", __func__, datalen);
+
+	if (datalen < 1)
+		return -EBADMSG;
+	p->version = *data;
+
+	if (p->version == PGP_SIG_VERSION_3) {
+		const struct pgp_signature_v3_packet *v3 = (const void *)data;
+
+		if (datalen < sizeof(*v3)) {
+			pr_debug("Short V3 signature packet\n");
+			return -EBADMSG;
+		}
+		datalen -= sizeof(*v3);
+		data += sizeof(*v3);
+
+		/* V3 has everything we need in the header */
+		p->signature_type = v3->hashed.signature_type;
+		memcpy(&p->issuer, &v3->issuer, 8);
+		p->pubkey_algo = v3->pubkey_algo;
+		p->hash_algo = v3->hash_algo;
+
+	} else if (p->version == PGP_SIG_VERSION_4) {
+		const struct pgp_signature_v4_packet *v4 = (const void *)data;
+		struct pgp_parse_sig_params_ctx ctx = {
+			.base.process_packet = pgp_process_sig_params_subpkt,
+			.params = p,
+			.got_the_issuer = false,
+		};
+		size_t subdatalen;
+
+		if (datalen < sizeof(*v4) + 2 + 2 + 2) {
+			pr_debug("Short V4 signature packet\n");
+			return -EBADMSG;
+		}
+		datalen -= sizeof(*v4);
+		data += sizeof(*v4);
+
+		/* V4 has most things in the header... */
+		p->signature_type = v4->signature_type;
+		p->pubkey_algo = v4->pubkey_algo;
+		p->hash_algo = v4->hash_algo;
+
+		/*
+		 * ... but we have to get the key ID from the subpackets, of
+		 * which there are two sets.
+		 */
+		__set_bit(PGP_SIG_ISSUER, ctx.base.types_of_interest);
+
+		subdatalen  = *data++ << 8;
+		subdatalen |= *data++;
+		datalen -= 2;
+		if (subdatalen) {
+			/* Hashed subpackets */
+			pr_devel("hashed data: %zu (after %zu)\n",
+				 subdatalen, sizeof(*v4));
+			if (subdatalen > datalen + 2 + 2) {
+				pr_debug("Short V4 signature packet [hdata]\n");
+				return -EBADMSG;
+			}
+			ret = pgp_parse_sig_subpkts(data, subdatalen,
+						    &ctx.base);
+			if (ret < 0)
+				return ret;
+			data += subdatalen;
+			datalen -= subdatalen;
+		}
+
+		subdatalen  = *data++ << 8;
+		subdatalen |= *data++;
+		datalen -= 2;
+		if (subdatalen) {
+			/* Unhashed subpackets */
+			pr_devel("unhashed data: %zu\n", subdatalen);
+			if (subdatalen > datalen + 2) {
+				pr_debug("Short V4 signature packet [udata]\n");
+				return -EBADMSG;
+			}
+			ret = pgp_parse_sig_subpkts(data, subdatalen,
+						    &ctx.base);
+			if (ret < 0)
+				return ret;
+			data += subdatalen;
+			datalen -= subdatalen;
+		}
+
+		if (!ctx.got_the_issuer) {
+			pr_debug("V4 signature packet lacks issuer\n");
+			return -EBADMSG;
+		}
+	} else {
+		pr_debug("Signature packet with unhandled version %d\n",
+			 p->version);
+		return -EBADMSG;
+	}
+
+	*_data = data;
+	*_datalen = datalen;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_sig_params);
diff --git a/crypto/asymmetric_keys/pgplib.h b/crypto/asymmetric_keys/pgplib.h
index 3ec4b408a11e..25191cea33a4 100644
--- a/crypto/asymmetric_keys/pgplib.h
+++ b/crypto/asymmetric_keys/pgplib.h
@@ -31,3 +31,28 @@ struct pgp_parse_pubkey {
 
 extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
 				struct pgp_parse_pubkey *pk);
+
+struct pgp_parse_sig_context {
+	unsigned long types_of_interest[128 / BITS_PER_LONG];
+	int (*process_packet)(struct pgp_parse_sig_context *context,
+			      enum pgp_sig_subpkt_type type,
+			      const u8 *data,
+			      size_t datalen);
+};
+
+extern int pgp_parse_sig_packets(const u8 *data, size_t datalen,
+				 struct pgp_parse_sig_context *ctx);
+
+struct pgp_sig_parameters {
+	enum pgp_signature_version version : 8;
+	enum pgp_signature_type signature_type : 8;
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	enum pgp_hash_algo hash_algo : 8;
+	union {
+		struct pgp_key_ID issuer;
+		__be32 issuer32[2];
+	};
+};
+
+extern int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
+				struct pgp_sig_parameters *p);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 06/14] KEYS: PGP data parser
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (4 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 05/14] PGPLIB: Signature parser Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 07/14] KEYS: Provide PGP key description autogeneration Roberto Sassu
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

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 9580), v4 keys only, 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 <penguin-kernel@I-love.SAKURA.ne.jp> for pointing
out some errors.

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig          |  10 +
 crypto/asymmetric_keys/Makefile         |   4 +
 crypto/asymmetric_keys/pgp_parser.h     |  18 ++
 crypto/asymmetric_keys/pgp_public_key.c | 356 ++++++++++++++++++++++++
 4 files changed, 388 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 8215e3fcd8db..c69bfdc022c0 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -109,4 +109,14 @@ config PGP_LIBRARY
 	  This option enables a library that provides a number of simple
 	  utility functions for parsing PGP (RFC 9580) packet-based messages.
 
+config PGP_KEY_PARSER
+	tristate "PGP key parser"
+	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select PGP_LIBRARY
+	select SHA1 # V4 fingerprint generation
+	help
+	  This option provides support for parsing PGP (RFC 9880) 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 000000000000..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 (dhowells@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 000000000000..94284a78bf9b
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Instantiate a public key crypto key from PGP format data [RFC 9580]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGP: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mpi.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/hash.h>
+#include <crypto/public_key.h>
+
+#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");
+MODULE_DESCRIPTION("PGP key parser");
+
+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",
+};
+
+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,
+};
+
+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,
+};
+
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
+{
+	crypto_shash_update(digest, &ch, 1);
+}
+
+struct pgp_key_data_parse_context {
+	struct pgp_parse_context pgp;
+	struct public_key *pub;
+	u8 raw_fingerprint[HASH_MAX_DIGESTSIZE];
+	size_t raw_fingerprint_len;
+};
+
+/*
+ * Calculate the public key ID (RFC 9580 5.5.4)
+ */
+static int pgp_calc_pkey_keyid(struct shash_desc *digest,
+			       struct pgp_parse_pubkey *pgp,
+			       struct public_key *pub)
+{
+	unsigned int nb[MAX_MPI];
+	unsigned int nn[MAX_MPI];
+	unsigned int n;
+	size_t keylen = pub->keylen;
+	u8 *key_ptr = pub->key;
+	u8 *pp[MAX_MPI];
+	u32 a32;
+	int npkey = pgp_key_algo_p_num_mpi[pgp->pubkey_algo];
+	int i, ret;
+
+	kenter("");
+
+	n = 6;
+	for (i = 0; i < npkey; i++) {
+		if (keylen < 2)
+			break;
+
+		ret = mpi_key_length(key_ptr, keylen, nb + i, nn + i);
+		if (ret < 0)
+			return ret;
+
+		key_ptr += 2;
+		keylen -= 2;
+		n += 2;
+
+		if (keylen < nn[i])
+			break;
+
+		pp[i] = key_ptr;
+		key_ptr += nn[i];
+		keylen -= nn[i];
+		n += nn[i];
+	}
+
+	if (keylen != 0) {
+		pr_debug("excess %zu\n", keylen);
+		return -EBADMSG;
+	}
+
+	digest_putc(digest, 0x99);     /* ctb */
+	digest_putc(digest, n >> 8);   /* 16-bit header length */
+	digest_putc(digest, n);
+	digest_putc(digest, pgp->version);
+
+	a32 = pgp->creation_time;
+	digest_putc(digest, a32 >> 24);
+	digest_putc(digest, a32 >> 16);
+	digest_putc(digest, a32 >>  8);
+	digest_putc(digest, a32 >>  0);
+
+	digest_putc(digest, pgp->pubkey_algo);
+
+	for (i = 0; i < npkey; i++) {
+		digest_putc(digest, nb[i] >> 8);
+		digest_putc(digest, nb[i]);
+		crypto_shash_update(digest, pp[i], nn[i]);
+	}
+	ret = 0;
+
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Calculate the public key ID fingerprint
+ */
+static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
+				    struct pgp_parse_pubkey *pgp,
+				    struct public_key *pub)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *digest;
+	char fingerprint[HASH_MAX_DIGESTSIZE * 2 + 1] = { 0 };
+	size_t offset;
+	int ret;
+
+	ret = -ENOMEM;
+	tfm = crypto_alloc_shash("sha1", 0, 0);
+	if (IS_ERR(tfm))
+		goto cleanup;
+
+	digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
+			 GFP_KERNEL);
+	if (!digest)
+		goto cleanup_tfm;
+
+	digest->tfm = tfm;
+	crypto_shash_set_flags(digest->tfm, CRYPTO_TFM_REQ_MAY_SLEEP);
+	ret = crypto_shash_init(digest);
+	if (ret < 0)
+		goto cleanup_hash;
+
+	ret = pgp_calc_pkey_keyid(digest, pgp, pub);
+	if (ret < 0)
+		goto cleanup_hash;
+
+	ctx->raw_fingerprint_len = crypto_shash_digestsize(tfm);
+
+	ret = crypto_shash_final(digest, ctx->raw_fingerprint);
+	if (ret < 0)
+		goto cleanup_hash;
+
+	offset = ctx->raw_fingerprint_len - 8;
+	pr_debug("offset %zu/%zu\n", offset, ctx->raw_fingerprint_len);
+
+	bin2hex(fingerprint, ctx->raw_fingerprint, ctx->raw_fingerprint_len);
+	pr_debug("fingerprint %s\n", fingerprint);
+
+	ret = 0;
+cleanup_hash:
+	kfree(digest);
+cleanup_tfm:
+	crypto_free_shash(tfm);
+cleanup:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Extract a public key or public subkey from the PGP stream.
+ */
+static int pgp_process_public_key(struct pgp_parse_context *context,
+				  enum pgp_packet_tag type,
+				  u8 headerlen,
+				  const u8 *data,
+				  size_t datalen)
+{
+	const char *algo;
+	struct pgp_key_data_parse_context *ctx =
+		container_of(context, struct pgp_key_data_parse_context, pgp);
+	struct pgp_parse_pubkey pgp;
+	struct public_key *pub;
+	u8 capabilities;
+	int ret;
+
+	kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+	if (ctx->raw_fingerprint_len) {
+		kleave(" = -ENOKEY [already]");
+		return -EBADMSG;
+	}
+
+	pub = kzalloc(sizeof(*pub), GFP_KERNEL);
+	if (!pub)
+		return -ENOMEM;
+	pub->id_type = "PGP";
+
+	ret = pgp_parse_public_key(&data, &datalen, &pgp);
+	if (ret < 0)
+		goto cleanup;
+
+	if (pgp.pubkey_algo >= PGP_PUBKEY__LAST)
+		goto cleanup_unsupported_pkey_algo;
+	algo = pgp_to_public_key_algo[pgp.pubkey_algo];
+	if (!algo)
+		goto cleanup_unsupported_pkey_algo;
+
+	pub->pkey_algo = algo;
+
+	/*
+	 * It's the public half of a key, so that only gives us encrypt and
+	 * verify capabilities.
+	 */
+	capabilities = pgp_public_key_capabilities[pgp.pubkey_algo] &
+		       (KEYCTL_SUPPORTS_ENCRYPT | KEYCTL_SUPPORTS_VERIFY);
+	/*
+	 * Capabilities are not stored anymore in the public key, consider
+	 * only keys that allow signature verification.
+	 */
+	if (!(capabilities & KEYCTL_SUPPORTS_VERIFY))
+		goto cleanup_unsupported_pkey_algo;
+
+	pub->key_eflags |= 1 << KEY_EFLAG_DIGITALSIG;
+
+	pub->key = kmemdup(data, datalen, GFP_KERNEL);
+	if (!pub->key)
+		goto cleanup_nomem;
+
+	pub->keylen = datalen;
+
+	ret = pgp_generate_fingerprint(ctx, &pgp, pub);
+	if (ret < 0)
+		goto cleanup;
+
+	ctx->pub = pub;
+	kleave(" = 0 [use]");
+	return 0;
+
+cleanup_unsupported_pkey_algo:
+	pr_debug("Unsupported public key algorithm %u\n",
+		 pgp.pubkey_algo);
+	ret = -ENOPKG;
+	goto cleanup;
+cleanup_nomem:
+	ret = -ENOMEM;
+	goto cleanup;
+cleanup:
+	pr_devel("cleanup");
+	public_key_free(pub);
+	kleave(" = %d", ret);
+	return ret;
+}
+
+static struct asymmetric_key_ids *
+pgp_key_generate_id(struct pgp_key_data_parse_context *ctx)
+{
+	struct asymmetric_key_ids *kids;
+	struct asymmetric_key_id *kid;
+
+	kids = kzalloc(sizeof(*kids), GFP_KERNEL);
+	if (!kids)
+		return kids;
+
+	kid = asymmetric_key_generate_id(ctx->raw_fingerprint,
+					 ctx->raw_fingerprint_len, NULL, 0);
+	if (IS_ERR(kid))
+		goto error;
+
+	kids->id[0] = kid;
+	kids->id[1] = kmemdup(kid, sizeof(*kid) + ctx->raw_fingerprint_len,
+			      GFP_KERNEL);
+	if (!kids->id[1])
+		goto error;
+
+	return kids;
+error:
+	kfree(kids->id[0]);
+	kfree(kids);
+
+	return NULL;
+}
+
+/*
+ * Attempt to parse the instantiation data blob for a key as a PGP packet
+ * message holding a key.
+ */
+static int pgp_key_parse(struct key_preparsed_payload *prep)
+{
+	struct pgp_key_data_parse_context ctx;
+	int ret;
+
+	kenter("");
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY);
+	ctx.pgp.process_packet = pgp_process_public_key;
+
+	ret = pgp_parse_packets(prep->data, prep->datalen, &ctx.pgp);
+	if (ret < 0)
+		goto error;
+
+	/* Key packet not found. */
+	if (!ctx.raw_fingerprint_len) {
+		ret = -ENOENT;
+		goto error;
+	}
+
+	/* We're pinning the module by being linked against it */
+	__module_get(public_key_subtype.owner);
+	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->quotalen = 100;
+	return 0;
+
+error:
+	public_key_free(ctx.pub);
+	return ret;
+}
+
+static struct asymmetric_key_parser pgp_key_parser = {
+	.owner		= THIS_MODULE,
+	.name		= "pgp",
+	.parse		= pgp_key_parse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pgp_key_init(void)
+{
+	return register_asymmetric_key_parser(&pgp_key_parser);
+}
+
+static void __exit pgp_key_exit(void)
+{
+	unregister_asymmetric_key_parser(&pgp_key_parser);
+}
+
+module_init(pgp_key_init);
+module_exit(pgp_key_exit);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 07/14] KEYS: Provide PGP key description autogeneration
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (5 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 06/14] KEYS: PGP data parser Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 08/14] KEYS: PGP-based public key signature verification Roberto Sassu
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

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 </tmp/key.pub

might create a key with a constructed description that can be seen in
/proc/keys:

2f674b96 I--Q---     1 perm 39390000     0     0 crypto    \
			Sample kernel key 31f0ae93: PGP.RSA 31f0ae93 []

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 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 94284a78bf9b..22f4a40c7eb7 100644
--- a/crypto/asymmetric_keys/pgp_public_key.c
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -54,6 +54,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;
 };
 
 /*
@@ -200,6 +202,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;
@@ -307,7 +318,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);
@@ -320,6 +332,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


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 08/14] KEYS: PGP-based public key signature verification
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (6 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 07/14] KEYS: Provide PGP key description autogeneration Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 09/14] KEYS: Retry asym key search with partial ID in restrict_link_by_signature() Roberto Sassu
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

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 <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 MAINTAINERS                            |   1 +
 crypto/asymmetric_keys/Makefile        |   3 +-
 crypto/asymmetric_keys/pgp_signature.c | 510 +++++++++++++++++++++++++
 include/crypto/pgp.h                   |  30 ++
 4 files changed, 543 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:	keyrings@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 000000000000..4b8df79f2968
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_signature.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* PGP public key signature verification [RFC 9580]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGPSIG: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mpi.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/key.h>
+#include <linux/err.h>
+#include <keys/asymmetric-type.h>
+#include <crypto/public_key.h>
+#include <crypto/hash.h>
+#include <crypto/pgp.h>
+
+#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 public key: \"%s\"\n", id + 3);
+
+	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 \"%s\" err %ld\n", id + 3,
+			 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_DOCUMENT_SIG &&
+	    tmp_params.signature_type != PGP_SIG_STANDALONE_SIG &&
+	    tmp_params.signature_type != PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY &&
+	    tmp_params.signature_type != PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY)
+		return 0;
+
+	memcpy(&ctx->params, &tmp_params, sizeof(tmp_params));
+	memcpy(&ctx->pkt, &tmp_pkt, sizeof(tmp_pkt));
+	return 0;
+}
+
+/**
+ * pgp_sig_parse - Begin the process of verifying a signature
+ * @sigdata: Signature blob
+ * @siglen: Length of signature blob
+ *
+ * This involves allocating the hash into which first the data and then the
+ * metadata will be put, and parsing the signature to get the issuer ID from
+ * which the key used to verify the signature will be searched.
+ *
+ * Return: A PGP sig context pointer on success, an error pointer on error.
+ */
+struct pgp_sig_verify *pgp_sig_parse(const u8 *sigdata, size_t siglen)
+{
+	struct pgp_sig_parse_context p;
+	struct pgp_sig_verify *ctx;
+	struct crypto_shash *tfm;
+	const char *pkey_algo;
+	size_t digest_size, desc_size;
+	int ret;
+
+	kenter(",,%zu", siglen);
+
+	p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+	p.pgp.process_packet = pgp_parse_signature;
+	p.pkt.data = NULL;
+	ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+	if (ret < 0) {
+		kleave(" = ERR_PTR [bad pkt]");
+		return ERR_PTR(ret);
+	}
+
+	if (!p.pkt.data) {
+		kleave(" = ERR_PTR [no pkt]");
+		return ERR_PTR(-ENOENT);
+	}
+
+	/* Check the signature itself for usefulness */
+	if (p.params.pubkey_algo >= PGP_PUBKEY__LAST)
+		goto unsupported_pkey_algo;
+	pkey_algo = pgp_to_public_key_algo[p.params.pubkey_algo];
+	if (!pkey_algo)
+		goto unsupported_pkey_algo;
+
+	if (p.params.hash_algo >= PGP_HASH__LAST ||
+	    !pgp_hash_algorithms[p.params.hash_algo]) {
+		pr_debug("Unsupported hash algorithm %u\n",
+			 p.params.hash_algo);
+		kleave(" = -ENOPKG [unsupp hash algo]");
+		return ERR_PTR(-ENOPKG);
+	}
+
+	pr_debug("Signature generated with %s hash\n",
+		 pgp_hash_algorithms[p.params.hash_algo]);
+
+	/*
+	 * Allocate the hashing algorithm we're going to need and find out how
+	 * big the hash operational data will be.
+	 */
+	tfm = crypto_alloc_shash(pgp_hash_algorithms[p.params.hash_algo], 0, 0);
+	if (IS_ERR(tfm)) {
+		ret = (PTR_ERR(tfm) == -ENOENT ? -ENOPKG : PTR_ERR(tfm));
+		kleave(" = %d", ret);
+		return ERR_PTR(ret);
+	}
+
+	desc_size = crypto_shash_descsize(tfm);
+	digest_size = crypto_shash_digestsize(tfm);
+
+	/*
+	 * We allocate the hash operational data storage on the end of our
+	 * context data.
+	 */
+	ctx = kzalloc(sizeof(*ctx) + sizeof(struct shash_desc) + desc_size,
+		      GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto error_have_shash;
+	}
+
+	ctx->sig = kzalloc(sizeof(*ctx->sig), GFP_KERNEL);
+	if (!ctx->sig) {
+		ret = -ENOMEM;
+		goto error_have_ctx;
+	}
+
+	ctx->sig->auth_ids[0] = asymmetric_key_generate_id(p.params.issuer.id,
+					sizeof(p.params.issuer.id), "", 0);
+	if (IS_ERR(ctx->sig->auth_ids[0])) {
+		ret = -ENOMEM;
+		goto error_have_ctx_sig;
+	}
+
+	ctx->sig->encoding	= "pkcs1";
+	ctx->sig->pkey_algo	= pkey_algo;
+	ctx->sig->hash_algo	= pgp_hash_algorithms[p.params.hash_algo];
+	ctx->sig->digest_size	= digest_size;
+	ctx->hash		= (struct shash_desc *)((void *)ctx +
+				  sizeof(*ctx));
+	ctx->hash->tfm		= tfm;
+	ctx->sig_version	= p.params.version;
+	ctx->sig_type		= p.params.signature_type;
+
+	memcpy(&ctx->pkt, &p.pkt, sizeof(p.pkt));
+
+	ret = crypto_shash_init(ctx->hash);
+	if (ret < 0)
+		goto error_have_auth_ids;
+
+	kleave(" = %p", ctx);
+	return ctx;
+
+error_have_auth_ids:
+	kfree(ctx->sig->auth_ids[0]);
+error_have_ctx_sig:
+	kfree(ctx->sig);
+error_have_ctx:
+	kfree(ctx);
+error_have_shash:
+	crypto_free_shash(tfm);
+	kleave(" = %d", ret);
+	return ERR_PTR(ret);
+
+unsupported_pkey_algo:
+	pr_debug("Unsupported public key algorithm %u\n",
+		 p.params.pubkey_algo);
+	kleave(" = -ENOPKG [unsupp pk algo]");
+	return ERR_PTR(-ENOPKG);
+}
+
+/*
+ * Load data into the hash
+ */
+int pgp_sig_add_data(struct pgp_sig_verify *ctx, const void *data,
+		     size_t datalen)
+{
+	return crypto_shash_update(ctx->hash, data, datalen);
+}
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * the hash; finalise the hash.
+ */
+static int pgp_digest_signature(struct pgp_sig_verify *ctx)
+{
+	enum pgp_signature_version version;
+	unsigned int nbytes, nbytes_alloc;
+	enum pgp_packet_tag type = ctx->pkt.type;
+	const u8 *data = ctx->pkt.data;
+	size_t datalen = ctx->pkt.datalen;
+	int ret;
+
+	kenter(",%u,,%zu", type, datalen);
+
+	if (ctx->sig->digest) {
+		kleave(" = 0 [digest found]");
+		return 0;
+	}
+
+	version = *data;
+	if (version == PGP_SIG_VERSION_3) {
+		/* We just include an excerpt of the metadata from a V3
+		 * signature.
+		 */
+		crypto_shash_update(ctx->hash, data + 2, 5);
+		data += sizeof(struct pgp_signature_v3_packet);
+		datalen -= sizeof(struct pgp_signature_v3_packet);
+	} else if (version == PGP_SIG_VERSION_4) {
+		/*
+		 * We add the whole metadata header and some of the hashed data
+		 * for a V4 signature, plus a trailer.
+		 */
+		size_t hashedsz, unhashedsz;
+		u8 trailer[6];
+
+		hashedsz = 4 + 2 + (data[4] << 8) + data[5];
+		crypto_shash_update(ctx->hash, data, hashedsz);
+
+		trailer[0] = version;
+		trailer[1] = 0xffU;
+		trailer[2] = hashedsz >> 24;
+		trailer[3] = hashedsz >> 16;
+		trailer[4] = hashedsz >> 8;
+		trailer[5] = hashedsz;
+
+		crypto_shash_update(ctx->hash, trailer, 6);
+		data += hashedsz;
+		datalen -= hashedsz;
+
+		unhashedsz = 2 + (data[0] << 8) + data[1];
+		data += unhashedsz;
+		datalen -= unhashedsz;
+	}
+
+	if (datalen <= 2) {
+		kleave(" = -EBADMSG");
+		return -EBADMSG;
+	}
+
+	/* There's a quick check on the hash available. */
+	ctx->signed_hash_msw[0] = *data++;
+	ctx->signed_hash_msw[1] = *data++;
+	datalen -= 2;
+
+	/*
+	 * And then the cryptographic data, which we'll need for the
+	 * algorithm.
+	 */
+	ret = mpi_key_length(data, datalen, NULL, &nbytes);
+	if (ret < 0) {
+		kleave(" = -EBADMSG [key length]");
+		return ret;
+	}
+
+	if (datalen != nbytes + 2) {
+		kleave(" = -EBADMSG [size mismatch]");
+		return -EBADMSG;
+	}
+
+	nbytes_alloc = DIV_ROUND_UP(nbytes, 8) * 8;
+
+	ctx->sig->s = kzalloc(nbytes_alloc, GFP_KERNEL);
+	if (!ctx->sig->s) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(ctx->sig->s + nbytes_alloc - nbytes, data + 2, nbytes);
+	ctx->sig->s_size = nbytes_alloc;
+
+	ctx->sig->digest = kmalloc(ctx->sig->digest_size, GFP_KERNEL);
+	if (!ctx->sig->digest) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = crypto_shash_final(ctx->hash, ctx->sig->digest);
+	if (ret < 0)
+		goto out;
+
+	pr_debug("hash: %*phN\n", ctx->sig->digest_size, ctx->sig->digest);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/**
+ * pgp_sig_get_digest - Finalize digest calculation
+ * @ctx: PGP sig verification context to use
+ * @buf: Buffer digest is written to
+ * @len: Buffer length
+ * @hash_algo: Digest algorithm
+ *
+ * Copy the calculated digest pointer, length and algorithm to the destinations
+ * provided by the caller.
+ *
+ * Return: 0 on success, a negative value on error.
+ */
+int pgp_sig_get_digest(struct pgp_sig_verify *ctx, const u8 **buf, u32 *len,
+		       enum hash_algo *hash_algo)
+{
+	int ret, i;
+
+	kenter("");
+
+	ret = pgp_digest_signature(ctx);
+	if (ret < 0)
+		goto out;
+
+	i = match_string(hash_algo_name, HASH_ALGO__LAST,
+			 ctx->sig->hash_algo);
+	if (i < 0) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	*hash_algo = i;
+	*buf = ctx->sig->digest;
+	*len = ctx->sig->digest_size;
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/**
+ * pgp_sig_verify - Verify the PGP signature
+ * @ctx: PGP sig verification context to use
+ * @keyring: Keyring containing the key for signature verification
+ *
+ * Search the key to be used for signature verification, and verify the PGP
+ * signature.
+ *
+ * Return: 0 if the signature is valid, a negative value otherwise.
+ */
+int pgp_sig_verify(struct pgp_sig_verify *ctx, struct key *keyring)
+{
+	const struct public_key *pub;
+	struct key *key;
+	int ret;
+
+	kenter("");
+
+	ret = pgp_digest_signature(ctx);
+	if (ret < 0)
+		goto out;
+
+	if (ctx->sig->digest[0] != ctx->signed_hash_msw[0] ||
+	    ctx->sig->digest[1] != ctx->signed_hash_msw[1]) {
+		pr_err("Hash (%02x%02x) mismatch against quick check (%02x%02x)\n",
+		       ctx->sig->digest[0], ctx->sig->digest[1],
+		       ctx->signed_hash_msw[0], ctx->signed_hash_msw[1]);
+		ret = -EKEYREJECTED;
+		goto out;
+	}
+
+	/* Now we need to find a key to use */
+	key = pgp_request_asymmetric_key(ctx, keyring);
+	if (IS_ERR(key)) {
+		ret = PTR_ERR(key);
+		goto out;
+	}
+
+	pub = key->payload.data[asym_crypto];
+
+	if (strcmp(ctx->sig->pkey_algo, pub->pkey_algo)) {
+		ret = -EKEYREJECTED;
+		goto out_key;
+	}
+
+	ret = verify_signature(key, ctx->sig);
+out_key:
+	key_put(key);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/**
+ * pgp_sig_verify_cancel - End the PGP signature verification
+ * @ctx: PGP sig verification context to use
+ * @keep_sig: Don't deallocate the signature
+ *
+ * Free the memory used for the signature verification. Keep only the signature,
+ * if requested.
+ */
+void pgp_sig_verify_cancel(struct pgp_sig_verify *ctx, bool keep_sig)
+{
+	kenter("");
+
+	crypto_free_shash(ctx->hash->tfm);
+	if (!keep_sig)
+		public_key_signature_free(ctx->sig);
+
+	kfree(ctx);
+
+	kleave("");
+}
+
+/**
+ * pgp_sig_get_sig - Return the PGP signature
+ * @ctx: PGP sig verification context to use
+ * @is_key_sig: Whether it is a public key signature
+ *
+ * Finalize the signature by calculating the digest if not already done. Then,
+ * return the PGP signature to the caller.
+ *
+ * Return: The PGP signature if successfully finalized, an error pointer
+ * otherwise.
+ */
+struct public_key_signature *pgp_sig_get_sig(struct pgp_sig_verify *ctx,
+					     bool is_key_sig)
+{
+	int ret;
+
+	if (is_key_sig &&
+	    ctx->sig_type != PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY &&
+	    ctx->sig_type != PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY)
+		return ERR_PTR(-EINVAL);
+
+	ret = pgp_digest_signature(ctx);
+	if (ret < 0)
+		return ERR_PTR(-ENOENT);
+
+	return ctx->sig;
+}
+
+/**
+ * pgp_sig_get_version - Return the PGP signature version
+ * @ctx: PGP sig verification context to use
+ *
+ * Return the version of the PGP signature to the caller.
+ *
+ * Return: The PGP signature version.
+ */
+u8 pgp_sig_get_version(struct pgp_sig_verify *ctx)
+{
+	return ctx->sig_version;
+}
diff --git a/include/crypto/pgp.h b/include/crypto/pgp.h
new file mode 100644
index 000000000000..99dd1ab6c1b1
--- /dev/null
+++ b/include/crypto/pgp.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* PGP signature processing
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _CRYPTO_PGP_H
+#define _CRYPTO_PGP_H
+
+#include <crypto/hash_info.h>
+
+struct key;
+struct pgp_sig_verify;
+
+/*
+ * pgp_signature.c
+ */
+extern struct pgp_sig_verify *pgp_sig_parse(const u8 *sigdata, size_t siglen);
+extern int pgp_sig_add_data(struct pgp_sig_verify *ctx,
+			    const void *data, size_t datalen);
+extern int pgp_sig_get_digest(struct pgp_sig_verify *ctx, const u8 **buf,
+			      u32 *len, enum hash_algo *hash_algo);
+extern int pgp_sig_verify(struct pgp_sig_verify *ctx, struct key *keyring);
+extern void pgp_sig_verify_cancel(struct pgp_sig_verify *ctx, bool keep_sig);
+extern struct public_key_signature *pgp_sig_get_sig(struct pgp_sig_verify *ctx,
+						    bool is_key_sig);
+extern u8 pgp_sig_get_version(struct pgp_sig_verify *ctx);
+
+#endif /* _CRYPTO_PGP_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 09/14] KEYS: Retry asym key search with partial ID in restrict_link_by_signature()
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (7 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 08/14] KEYS: PGP-based public key signature verification Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 10/14] KEYS: Calculate key digest and get signature of the key Roberto Sassu
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

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 RFC 9580 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 <roberto.sassu@huawei.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---
 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


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 10/14] KEYS: Calculate key digest and get signature of the key
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (8 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 09/14] KEYS: Retry asym key search with partial ID in restrict_link_by_signature() Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 11/14] verification: introduce verify_pgp_signature() Roberto Sassu
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

Calculate the digest of the signature, according to the RFC 9580 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 <roberto.sassu@huawei.com>
---
 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 22f4a40c7eb7..2dc8b5d321e9 100644
--- a/crypto/asymmetric_keys/pgp_public_key.c
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -14,6 +14,7 @@
 #include <keys/asymmetric-parser.h>
 #include <crypto/hash.h>
 #include <crypto/public_key.h>
+#include <crypto/pgp.h>
 
 #include "pgp_parser.h"
 
@@ -56,6 +57,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;
 };
 
 /*
@@ -216,6 +219,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;
@@ -306,6 +315,77 @@ pgp_key_generate_id(struct pgp_key_data_parse_context *ctx)
 	return NULL;
 }
 
+/*
+ * Calculate the digest of the signature according to the RFC 9580, section
+ * 5.2.4 (packet types 0x10 and 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.
@@ -370,6 +450,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


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 11/14] verification: introduce verify_pgp_signature()
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (9 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 10/14] KEYS: Calculate key digest and get signature of the key Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 12/14] PGP: Provide a key type for testing PGP signatures Roberto Sassu
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

Introduce verify_pgp_signature() to verify PGP signatures from detached
data. It can be used by kernel subsystems (e.g. IMA).

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 certs/system_keyring.c       | 71 ++++++++++++++++++++++++++++++++++++
 include/linux/verification.h | 23 ++++++++++++
 2 files changed, 94 insertions(+)

diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index 9de610bf1f4b..f132773c6096 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -16,6 +16,7 @@
 #include <keys/asymmetric-type.h>
 #include <keys/system_keyring.h>
 #include <crypto/pkcs7.h>
+#include <crypto/pgp.h>
 
 static struct key *builtin_trusted_keys;
 #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
@@ -418,6 +419,76 @@ 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,
+					(void *)2UL for platform 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(const void *pebuf, unsigned pelen,
 				   struct key *trusted_keys,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 12/14] PGP: Provide a key type for testing PGP signatures
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (10 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 11/14] verification: introduce verify_pgp_signature() Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 13/14] KEYS: Provide a function to load keys from a PGP keyring blob Roberto Sassu
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

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

A suitable message can be generated like this:

	echo "This is a test attached-signed content" >content.txt
	gpg --compress-algo=none -s content.txt

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig        |  15 +++
 crypto/asymmetric_keys/Makefile       |   2 +
 crypto/asymmetric_keys/pgp_library.c  |  64 +++++++++++++
 crypto/asymmetric_keys/pgp_test_key.c | 131 ++++++++++++++++++++++++++
 crypto/asymmetric_keys/pgplib.h       |  16 ++++
 5 files changed, 228 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp_test_key.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index c69bfdc022c0..d28b9593a70d 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -119,4 +119,19 @@ 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 loaded 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.  If the key used for signature verification isn't in the
+	  built-in or secondary keyring, adding a key will succeed but this
+	  fact will be signaled through a debug message in the kernel log.
+
+	  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 a9708ccbcb81..ff4bb52790dd 100644
--- a/crypto/asymmetric_keys/pgp_library.c
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -544,3 +544,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 9580: 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 000000000000..80ab72ed09b9
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_test_key.c
@@ -0,0 +1,131 @@
+// 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 (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PGPtest: "fmt
+#include <linux/key.h>
+#include <linux/key-type.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/verification.h>
+#include <keys/user-type.h>
+#include <keys/system_keyring.h>
+#include <crypto/pgp.h>
+
+#include "pgp_parser.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PGP testing key type");
+
+struct pgp_test_parse_context {
+	struct pgp_parse_context pgp;
+	struct pgp_literal_data_parameters params;
+	const void *content;
+};
+
+static int pgp_test_parse_data(struct pgp_parse_context *context,
+			       enum pgp_packet_tag type,
+			       u8 headerlen,
+			       const u8 *data,
+			       size_t datalen)
+{
+	struct pgp_test_parse_context *ctx =
+		container_of(context, struct pgp_test_parse_context, pgp);
+	int ret;
+
+	kenter("");
+
+	ret = pgp_parse_literal_data(data, datalen, &ctx->params);
+	if (ret == 0)
+		ctx->content = data + ctx->params.content_offset;
+	return ret;
+}
+
+/*
+ * Instantiate a PGP wrapped and validated key.
+ */
+static int pgp_test_instantiate(struct key *key,
+				struct key_preparsed_payload *prep)
+{
+	struct pgp_test_parse_context p;
+	const void *saved_prep_data;
+	size_t saved_prep_datalen;
+	const struct cred *cred = current_cred();
+	int ret;
+
+	kenter("");
+
+	memset(&p, 0, sizeof(p));
+	p.pgp.types_of_interest = (1 << PGP_PKT_LITERAL_DATA);
+	p.pgp.process_packet = pgp_test_parse_data;
+	ret = pgp_parse_packets(prep->data, prep->datalen, &p.pgp);
+	if (ret < 0) {
+		kleave(" = %d [parse]", ret);
+		return ret;
+	}
+
+	if (!p.params.content_len) {
+		kleave(" = -ENODATA [no literal data");
+		return -ENODATA;
+	}
+
+	ret = verify_pgp_signature(p.content, p.params.content_len,
+				   prep->data, prep->datalen,
+				   VERIFY_USE_SECONDARY_KEYRING,
+				   VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
+	if (ret < 0 && cred->session_keyring) {
+		ret = verify_pgp_signature(p.content, p.params.content_len,
+					   prep->data, prep->datalen,
+					   cred->session_keyring,
+					   VERIFYING_UNSPECIFIED_SIGNATURE,
+					   NULL, NULL);
+		if (ret < 0)
+			goto error;
+
+		pr_warn("PGP message doesn't chain back to a trusted key\n");
+	}
+
+	saved_prep_data = prep->data;
+	saved_prep_datalen = prep->datalen;
+	prep->data = p.content;
+	prep->datalen = p.params.content_len;
+	ret = generic_key_instantiate(key, prep);
+	prep->data = saved_prep_data;
+	prep->datalen = saved_prep_datalen;
+error:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * user defined keys take an arbitrary string as the description and an
+ * arbitrary blob of data as the payload
+ */
+static struct key_type key_type_pgp_test = {
+	.name			= "pgp_test",
+	.instantiate		= pgp_test_instantiate,
+	.revoke			= user_revoke,
+	.destroy		= user_destroy,
+	.describe		= user_describe,
+	.read			= user_read,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pgp_key_init(void)
+{
+	return register_key_type(&key_type_pgp_test);
+}
+
+static void __exit pgp_key_cleanup(void)
+{
+	unregister_key_type(&key_type_pgp_test);
+}
+
+module_init(pgp_key_init);
+module_exit(pgp_key_cleanup);
diff --git a/crypto/asymmetric_keys/pgplib.h b/crypto/asymmetric_keys/pgplib.h
index 25191cea33a4..9c852256d1c9 100644
--- a/crypto/asymmetric_keys/pgplib.h
+++ b/crypto/asymmetric_keys/pgplib.h
@@ -56,3 +56,19 @@ struct pgp_sig_parameters {
 
 extern int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
 				struct pgp_sig_parameters *p);
+
+#if IS_ENABLED(CONFIG_PGP_TEST_KEY)
+
+struct pgp_literal_data_parameters {
+	enum pgp_literal_data_format format : 8;
+	u8 filename_len;
+	u8 filename_offset;
+	u8 content_offset;
+	u32 content_len;
+	u32 time;
+};
+
+extern int pgp_parse_literal_data(const u8 *data, size_t datalen,
+				  struct pgp_literal_data_parameters *p);
+
+#endif /* CONFIG_PGP_TEST_KEY */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 13/14] KEYS: Provide a function to load keys from a PGP keyring blob
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (11 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 12/14] PGP: Provide a key type for testing PGP signatures Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-11 12:29 ` [PATCH v3 14/14] KEYS: Introduce load_pgp_public_keyring() Roberto Sassu
  2024-09-13  4:45 ` [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Herbert Xu
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: David Howells <dhowells@redhat.com>

Provide a function to load keys from a PGP keyring blob to the built-in and
.ima keyrings:

	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 1f030000     0     0 asymmetri \
				Red Hat, Inc. dbeca166: PGP.DSA dbeca166 []

Thanks to Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> for some
pointing out some errors.

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 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 d28b9593a70d..fc1a0d692a53 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -134,4 +134,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 and .ima keyrings.
+
 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 000000000000..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 (dhowells@redhat.com)
+ */
+
+#include <linux/module.h>
+#include <linux/key.h>
+#include <linux/err.h>
+#include <crypto/pgp.h>
+#include <keys/asymmetric-type.h>
+
+#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 description is mostly irrelevant apart from the
+ * fact that keys of the same description displace one another from a keyring.
+ *
+ * The caller should override the current creds if they want the keys to be
+ * owned by someone other than the current process's owner.  Keys will not be
+ * accounted towards the owner's quota.
+ *
+ * This function may only be called whilst the kernel is booting.
+ */
+int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+			    struct key *keyring)
+{
+	struct preload_pgp_keys_context ctx;
+	int ret;
+
+	ctx.pgp.types_of_interest = (1 << PGP_PKT_PUBLIC_KEY);
+	ctx.pgp.process_packet = found_pgp_key;
+	ctx.keyring = make_key_ref(keyring, 1);
+	ctx.found_key = false;
+
+	ret = pgp_parse_packets(pgpdata, pgpdatalen, &ctx.pgp);
+	if (ret < 0)
+		return ret;
+
+	if (ctx.found_key) {
+		ctx.key_end = pgpdata + pgpdatalen;
+		create_pgp_key(&ctx);
+	}
+	return 0;
+}
diff --git a/include/crypto/pgp.h b/include/crypto/pgp.h
index 99dd1ab6c1b1..263b44603636 100644
--- a/include/crypto/pgp.h
+++ b/include/crypto/pgp.h
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
-/* PGP signature processing
+/* PGP key and signature processing
  *
  * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
@@ -27,4 +27,10 @@ extern struct public_key_signature *pgp_sig_get_sig(struct pgp_sig_verify *ctx,
 						    bool is_key_sig);
 extern u8 pgp_sig_get_version(struct pgp_sig_verify *ctx);
 
+/*
+ * pgp_preload.c
+ */
+extern int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+				   struct key *keyring);
+
 #endif /* _CRYPTO_PGP_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH v3 14/14] KEYS: Introduce load_pgp_public_keyring()
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (12 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 13/14] KEYS: Provide a function to load keys from a PGP keyring blob Roberto Sassu
@ 2024-09-11 12:29 ` Roberto Sassu
  2024-09-13  4:45 ` [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Herbert Xu
  14 siblings, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-11 12:29 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

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

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 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 f132773c6096..357d52d1d250 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


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 01/14] mpi: Introduce mpi_key_length()
  2024-09-11 12:28 ` [PATCH v3 01/14] mpi: Introduce mpi_key_length() Roberto Sassu
@ 2024-09-12 13:26   ` Jarkko Sakkinen
  0 siblings, 0 replies; 37+ messages in thread
From: Jarkko Sakkinen @ 2024-09-12 13:26 UTC (permalink / raw)
  To: Roberto Sassu, dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

On Wed Sep 11, 2024 at 3:28 PM EEST, Roberto Sassu wrote:
> From: Roberto Sassu <roberto.sassu@huawei.com>
>
> Introduce the new function to get the number of bits and bytes from an MPI.
>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>  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);

"_arg" - what is the point of this?

>  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);

BR, Jarkko

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 02/14] rsa: add parser of raw format
  2024-09-11 12:28 ` [PATCH v3 02/14] rsa: add parser of raw format Roberto Sassu
@ 2024-09-12 13:33   ` Jarkko Sakkinen
  0 siblings, 0 replies; 37+ messages in thread
From: Jarkko Sakkinen @ 2024-09-12 13:33 UTC (permalink / raw)
  To: Roberto Sassu, dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

On Wed Sep 11, 2024 at 3:28 PM EEST, Roberto Sassu wrote:
> From: Roberto Sassu <roberto.sassu@huawei.com>
>
> Parse the RSA key with RAW format if the ASN.1 parser returns an error, to
> avoid passing somehow the key format as parameter.
>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>  crypto/rsa.c                  | 14 ++++--
>  crypto/rsa_helper.c           | 83 ++++++++++++++++++++++++++++++++++-
>  include/crypto/internal/rsa.h |  6 +++
>  3 files changed, 97 insertions(+), 6 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..40a17ebc972f 100644
> --- a/crypto/rsa_helper.c
> +++ b/crypto/rsa_helper.c
> @@ -9,6 +9,7 @@
>  #include <linux/export.h>
>  #include <linux/err.h>
>  #include <linux/fips.h>
> +#include <linux/mpi.h>
>  #include <crypto/internal/rsa.h>
>  #include "rsapubkey.asn1.h"
>  #include "rsaprivkey.asn1.h"
> @@ -148,6 +149,42 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag,
>  	return 0;
>  }
>  

/*
 * Please, document me.
 */
> +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++) {
> +		if (key_len < 2)
> +			return -EINVAL;
> +
> +		ret = mpi_key_length(key_ptr, len, NULL, &nbytes);
> +		if (ret < 0)
> +			return ret;
> +
> +		key_ptr += 2;
> +		key_len -= 2;
> +
> +		if (key_len < nbytes)
> +			return -EINVAL;
> +
> +		ret = func[i](rsa_key, 0, 0, key_ptr, nbytes);
> +		if (ret < 0)
> +			return ret;
> +
> +		key_ptr += nbytes;
> +		key_len -= nbytes;
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the
>   *                       provided struct rsa_key, pointers to the raw key as is,
> @@ -157,7 +194,7 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag,
>   * @key:	key in BER format
>   * @key_len:	length of key
>   *
> - * Return:	0 on success or error code in case of error
> + * Return:	0 on success or error code in case of error.
>   */
>  int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
>  		      unsigned int key_len)
> @@ -166,6 +203,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
> @@ -176,7 +234,7 @@ EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
>   * @key:	key in BER format
>   * @key_len:	length of key
>   *
> - * Return:	0 on success or error code in case of error
> + * Return:	0 on success or error code in case of error.
>   */
>  int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
>  		       unsigned int key_len)
> @@ -184,3 +242,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.

This belongs after the parameters, here a one-liner would be a better
choice:

https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt

Try to avoid "etc" in documentation because then I have no choice than
stop reading your documentation and read source code instead.

Just enumerate everything that makes sense, no aim for perfection but
it is like a stamp that I tried my best, and then we will refine the
description.

"etc" in the documentation encompasses also all the future changes,
which does not encourage accuracy :-) Imcompleteness is less of a
devil than entropy, or at least that is how I weight these matters.

> + *
> + * @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..7141e806ceea 100644
> --- a/include/crypto/internal/rsa.h
> +++ b/include/crypto/internal/rsa.h
> @@ -50,8 +50,14 @@ struct rsa_key {
>  int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
>  		      unsigned int key_len);
>  
> +int rsa_parse_pub_key_raw(struct rsa_key *rsa_key, const void *key,
> +			  unsigned int key_len);
> +
>  int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
>  		       unsigned int key_len);
>  
> +int rsa_parse_priv_key_raw(struct rsa_key *rsa_key, const void *key,
> +			   unsigned int key_len);
> +
>  extern struct crypto_template rsa_pkcs1pad_tmpl;
>  #endif


BR, Jarkko

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 03/14] PGPLIB: PGP definitions (RFC 9580)
  2024-09-11 12:29 ` [PATCH v3 03/14] PGPLIB: PGP definitions (RFC 9580) Roberto Sassu
@ 2024-09-12 13:54   ` Jarkko Sakkinen
  0 siblings, 0 replies; 37+ messages in thread
From: Jarkko Sakkinen @ 2024-09-12 13:54 UTC (permalink / raw)
  To: Roberto Sassu, dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

On Wed Sep 11, 2024 at 3:29 PM EEST, Roberto Sassu wrote:
> From: David Howells <dhowells@redhat.com>
>
> Provide some useful PGP definitions from RFC 9580.  These describe details
> of public key crypto as used by crypto keys for things like signature
> verification.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  crypto/asymmetric_keys/pgp.h | 216 +++++++++++++++++++++++++++++++++++
>  1 file changed, 216 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 000000000000..eaf0ab8e0373
> --- /dev/null
> +++ b/crypto/asymmetric_keys/pgp.h
> @@ -0,0 +1,216 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* PGP definitions (RFC 9580)
> + *
> + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + */
> +
> +#include <linux/types.h>
> +
> +struct pgp_key_ID {
> +	u8 id[8];
> +} __packed;
> +
> +struct pgp_time {
> +	u8 time[4];
> +} __packed;
> +
> +/*
> + * PGP public-key algorithm identifiers [RFC 9580: 9.1]

Nicely done documentation, which is rare, i.e. tells what the thing is
and gives an easy to access reference. 0x1F44D for that.

> + */
> +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_ECDH			= 18,
> +	PGP_PUBKEY_ECDSA		= 19,
> +	PGP_PUBKEY_EDDSA_LEGACY		= 22,
> +	PGP_PUBKEY_X25519		= 25,
> +	PGP_PUBKEY_X448			= 26,
> +	PGP_PUBKEY_ED25519		= 27,
> +	PGP_PUBKEY_ED448		= 28,
> +	PGP_PUBKEY__LAST
> +};
> +
> +/*
> + * PGP symmetric-key algorithm identifiers [RFC 9580: 9.3]
> + */
> +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_SYMKEY_CAMELIA_128KEY	= 11,
> +	PGP_SYMKEY_CAMELIA_192KEY	= 12,
> +	PGP_SYMKEY_CAMELIA_256KEY	= 13,
> +	PGP_SYMKEY__LAST
> +};
> +
> +/*
> + * PGP compression algorithm identifiers [RFC 9580: 9.4]
> + */
> +enum pgp_compr_algo {
> +	PGP_COMPR_UNCOMPRESSED		= 0,
> +	PGP_COMPR_ZIP			= 1,
> +	PGP_COMPR_ZLIB			= 2,
> +	PGP_COMPR_BZIP2			= 3,
> +	PGP_COMPR__LAST
> +};
> +
> +/*
> + * PGP hash algorithm identifiers [RFC 9580: 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_SHA3_256		= 12,
> +	PGP_HASH_SHA3_512		= 14,
> +	PGP_HASH__LAST
> +};
> +

/*
 * doc
 */
> +extern const char *const pgp_hash_algorithms[PGP_HASH__LAST];
> +
> +/*
> + * PGP packet type tags [RFC 9580: 5].
> + */
> +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 [RFC 9580: 5.2].
> + */
> +enum pgp_signature_version {
> +	PGP_SIG_VERSION_3			= 3,
> +	PGP_SIG_VERSION_4			= 4,
> +};
> +
> +/*
> + * Signature types [RFC 9580: 5.2.1].
> + */
> +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,
> +};
> +

/*
 * doc
 */
> +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 issuer;
> +	enum pgp_pubkey_algo pubkey_algo : 8;
> +	enum pgp_hash_algo hash_algo : 8;
> +} __packed;
> +

/*
 * doc
 */
> +struct pgp_signature_v4_packet {
> +	enum pgp_signature_version version : 8;	/* == PGP_SIG_VERSION_4 */
> +	enum pgp_signature_type signature_type : 8;
> +	enum pgp_pubkey_algo pubkey_algo : 8;
> +	enum pgp_hash_algo hash_algo : 8;
> +} __packed;
> +
> +/*
> + * V4 signature subpacket types [RFC 9580: 5.2.3.7].
> + */
> +enum pgp_sig_subpkt_type {
> +	PGP_SIG_CREATION_TIME			= 2,
> +	PGP_SIG_EXPIRATION_TIME			= 3,
> +	PGP_SIG_EXPORTABLE_CERT			= 4,
> +	PGP_SIG_TRUST_SIG			= 5,
> +	PGP_SIG_REGEXP				= 6,
> +	PGP_SIG_REVOCABLE			= 7,
> +	PGP_SIG_KEY_EXPIRATION_TIME		= 9,
> +	PGP_SIG_PREF_SYM_ALGO			= 11,
> +	PGP_SIG_REVOCATION_KEY			= 12,
> +	PGP_SIG_ISSUER				= 16,
> +	PGP_SIG_NOTATION_DATA			= 20,
> +	PGP_SIG_PREF_HASH_ALGO			= 21,
> +	PGP_SIG_PREF_COMPR_ALGO			= 22,
> +	PGP_SIG_KEY_SERVER_PREFS		= 23,
> +	PGP_SIG_PREF_KEY_SERVER			= 24,
> +	PGP_SIG_PRIMARY_USER_ID			= 25,
> +	PGP_SIG_POLICY_URI			= 26,
> +	PGP_SIG_KEY_FLAGS			= 27,
> +	PGP_SIG_SIGNERS_USER_ID			= 28,
> +	PGP_SIG_REASON_FOR_REVOCATION		= 29,
> +	PGP_SIG_FEATURES			= 30,
> +	PGP_SIG_TARGET				= 31,
> +	PGP_SIG_EMBEDDED_SIG			= 32,
> +	PGP_SIG_ISSUER_FINGERPRINT		= 33,
> +	PGP_SIG_INTENDED_RECIPIENT_FINGERPRINT	= 35,
> +	PGP_SIG_PREFERRED_AEAD_CIPHERS		= 39,
> +	PGP_SIG__LAST
> +};
> +

/*
 * Documentation what is the critical mask.
 */
> +#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK	0x80
> +
> +/*
> + * Key (tag 5, 6, 7 and 14) packet
> + */
> +enum pgp_key_version {
> +	PGP_KEY_VERSION_4			= 4,
> +};
> +

/*
 * doc
 */
> +struct pgp_key_v4_packet {
> +	enum pgp_key_version version : 8;
> +	struct pgp_time	creation_time;
> +	enum pgp_pubkey_algo pubkey_algo : 8;
> +	u8 key_material[];
> +} __packed;
> +
> +/*
> + * Literal Data (tag 11) packet
> + */
> +enum pgp_literal_data_format {
> +	PGP_LIT_FORMAT_BINARY			= 0x62,
> +	PGP_LIT_FORMAT_TEXT			= 0x74,
> +	PGP_LIT_FORMAT_TEXT_UTF8		= 0x75,
> +};


BR, Jarkko

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 04/14] PGPLIB: Basic packet parser
  2024-09-11 12:29 ` [PATCH v3 04/14] PGPLIB: Basic packet parser Roberto Sassu
@ 2024-09-12 13:57   ` Jarkko Sakkinen
  0 siblings, 0 replies; 37+ messages in thread
From: Jarkko Sakkinen @ 2024-09-12 13:57 UTC (permalink / raw)
  To: Roberto Sassu, dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

On Wed Sep 11, 2024 at 3:29 PM EEST, Roberto Sassu wrote:
> From: David Howells <dhowells@redhat.com>
>
> 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 <dhowells@redhat.com>
> Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  crypto/asymmetric_keys/Kconfig       |   6 +
>  crypto/asymmetric_keys/Makefile      |   5 +
>  crypto/asymmetric_keys/pgp_library.c | 262 +++++++++++++++++++++++++++
>  crypto/asymmetric_keys/pgplib.h      |  33 ++++
>  4 files changed, 306 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..8215e3fcd8db 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 9580) 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 000000000000..1b87f8af411b
> --- /dev/null
> +++ b/crypto/asymmetric_keys/pgp_library.c
> @@ -0,0 +1,262 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* PGP packet parser (RFC 9580)
/* 
 * PGP packet parser (RFC 9580)

> + *
> + * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + */
> +
> +#define pr_fmt(fmt) "PGPL: "fmt
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include "pgplib.h"
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("PGP library");
> +
> +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",
> +	[PGP_HASH_SHA3_256]		= "sha3-256",
> +	[PGP_HASH_SHA3_512]		= "sha3-512",
> +};
> +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 9580: 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;

Move the last declaration as the first declaration, makes more
readable (reverse christmas tree order).

I don't have time to go this patch fully for this round but I
saw some other similar nits below.

> +
> +	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 ... 0xdf:
> +			/* Two-byte length */
> +			if (datalen < 2)
> +				goto short_packet;
> +			size = (data[0] - 192) * 256;
> +			size += data[1] + 192;
> +			data += 2;
> +			datalen -= 2;
> +			*_headerlen = 3;
> +			break;
> +		case 0xff:
> +			/* Five-byte length */
> +			if (datalen < 5)
> +				goto short_packet;
> +			size =  data[1] << 24;
> +			size |= data[2] << 16;
> +			size |= data[3] << 8;
> +			size |= data[4];
> +			data += 5;
> +			datalen -= 5;
> +			*_headerlen = 6;
> +			break;
> +		default:
> +			pr_debug("Partial body length packet not supported\n");
> +			return -EBADMSG;
> +		}
> +	} else {
> +		/* Old packet length format */
> +		u8 length_type = type & 0x03;
> +
> +		type >>= 2;
> +		pr_devel("old format: t=%u lt=%u\n", type, length_type);
> +
> +		switch (length_type) {
> +		case 0:
> +			/* One-byte length */
> +			size = data[0];
> +			data++;
> +			datalen--;
> +			*_headerlen = 2;
> +			break;
> +		case 1:
> +			/* Two-byte length */
> +			if (datalen < 2)
> +				goto short_packet;
> +			size  = data[0] << 8;
> +			size |= data[1];
> +			data += 2;
> +			datalen -= 2;
> +			*_headerlen = 3;
> +			break;
> +		case 2:
> +			/* Four-byte length */
> +			if (datalen < 4)
> +				goto short_packet;
> +			size  = data[0] << 24;
> +			size |= data[1] << 16;
> +			size |= data[2] << 8;
> +			size |= data[3];
> +			data += 4;
> +			datalen -= 4;
> +			*_headerlen = 5;
> +			break;
> +		default:
> +			pr_debug("Indefinite length packet not supported\n");
> +			return -EBADMSG;
> +		}
> +	}
> +
> +	pr_devel("datalen=%zu size=%zu\n", datalen, size);
> +	if (datalen < size)
> +		goto short_packet;
> +	if (size > INT_MAX)
> +		goto too_big;
> +
> +	*_data = data;
> +	*_datalen = datalen;
> +	*_type = type;
> +	pr_devel("Found packet type=%u size=%zd\n", type, size);
> +	return size;
> +
> +short_packet:
> +	pr_debug("Attempt to parse short packet\n");
> +	return -EBADMSG;
> +too_big:
> +	pr_debug("Signature subpacket size >2G\n");
> +	return -EMSGSIZE;
> +}
> +
> +/**
> + * pgp_parse_packets - Parse a set of PGP packets
> + * @data: Data to be parsed (updated)
> + * @datalen: Amount of data (updated)
> + * @ctx: Parsing context
> + *
> + * Parse a set of PGP packets [RFC 9580: 4].
> + *
> + * Return: 0 on successful parsing, a negative value otherwise.
> + */
> +int pgp_parse_packets(const u8 *data, size_t datalen,
> +		      struct pgp_parse_context *ctx)
> +{
> +	enum pgp_packet_tag type;
> +	ssize_t pktlen;
> +	u8 headerlen;
> +	int ret;
> +
> +	while (datalen > 2) {
> +		pktlen = pgp_parse_packet_header(&data, &datalen, &type,
> +						 &headerlen);
> +		if (pktlen < 0)
> +			return pktlen;
> +
> +		if ((ctx->types_of_interest >> type) & 1) {
> +			ret = ctx->process_packet(ctx, type, headerlen,
> +						  data, pktlen);
> +			if (ret < 0)
> +				return ret;
> +		}
> +		data += pktlen;
> +		datalen -= pktlen;
> +	}
> +
> +	if (datalen != 0) {
> +		pr_debug("Excess octets in packet stream\n");
> +		return -EBADMSG;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pgp_parse_packets);
> +
> +/**
> + * pgp_parse_public_key - Parse the common part of a PGP pubkey packet
> + * @_data: Content of packet (updated)
> + * @_datalen: Length of packet remaining (updated)
> + * @pk: Public key data
> + *
> + * Parse the common data struct for a PGP pubkey packet [RFC 9580: 5.5.2].
> + *
> + * Return: 0 on successful parsing, a negative value otherwise.
> + */
> +int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
> +			 struct pgp_parse_pubkey *pk)
> +{
> +	const u8 *data = *_data;
> +	size_t datalen = *_datalen;
> +	unsigned int tmp;
> +
> +	if (datalen < 12) {
> +		pr_debug("Public key packet too short\n");
> +		return -EBADMSG;
> +	}
> +
> +	pk->version = *data++;
> +	switch (pk->version) {
> +	case PGP_KEY_VERSION_4:
> +		break;
> +	default:
> +		pr_debug("Public key packet with unhandled version %d\n",
> +			 pk->version);
> +		return -EBADMSG;
> +	}
> +
> +	tmp  = *data++ << 24;
> +	tmp |= *data++ << 16;
> +	tmp |= *data++ << 8;
> +	tmp |= *data++;
> +	pk->creation_time = tmp;
> +	if (pk->version == PGP_KEY_VERSION_4)
> +		pk->expires_at = 0; /* Have to get it from the selfsignature */
> +
> +	pk->pubkey_algo = *data++;
> +	datalen -= 6;
> +
> +	pr_devel("%x,%x,%lx,%lx\n",
> +		 pk->version, pk->pubkey_algo, pk->creation_time,
> +		 pk->expires_at);
> +
> +	*_data = data;
> +	*_datalen = datalen;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pgp_parse_public_key);
> diff --git a/crypto/asymmetric_keys/pgplib.h b/crypto/asymmetric_keys/pgplib.h
> new file mode 100644
> index 000000000000..3ec4b408a11e
> --- /dev/null
> +++ b/crypto/asymmetric_keys/pgplib.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* PGP library definitions (RFC 9580)
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + */
> +
> +#include "pgp.h"
> +
> +/*
> + * PGP library packet parser
> + */
> +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);
> +};
> +
> +extern int pgp_parse_packets(const u8 *data, size_t datalen,
> +			     struct pgp_parse_context *ctx);
> +
> +struct pgp_parse_pubkey {
> +	enum pgp_key_version version : 8;
> +	enum pgp_pubkey_algo pubkey_algo : 8;
> +	__kernel_old_time_t creation_time;
> +	__kernel_old_time_t expires_at;
> +};
> +
> +extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
> +				struct pgp_parse_pubkey *pk);


BR, Jarkko

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 05/14] PGPLIB: Signature parser
  2024-09-11 12:29 ` [PATCH v3 05/14] PGPLIB: Signature parser Roberto Sassu
@ 2024-09-12 13:58   ` Jarkko Sakkinen
  0 siblings, 0 replies; 37+ messages in thread
From: Jarkko Sakkinen @ 2024-09-12 13:58 UTC (permalink / raw)
  To: Roberto Sassu, dhowells, dwmw2, herbert, davem
  Cc: linux-kernel, keyrings, linux-crypto, zohar, linux-integrity,
	torvalds, Roberto Sassu

On Wed Sep 11, 2024 at 3:29 PM EEST, Roberto Sassu wrote:
> From: David Howells <dhowells@redhat.com>
>
> 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 <dhowells@redhat.com>
> Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>  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 1b87f8af411b..a9708ccbcb81 100644
> --- a/crypto/asymmetric_keys/pgp_library.c
> +++ b/crypto/asymmetric_keys/pgp_library.c
> @@ -260,3 +260,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
                                 ()

BR, Jarkko

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
                   ` (13 preceding siblings ...)
  2024-09-11 12:29 ` [PATCH v3 14/14] KEYS: Introduce load_pgp_public_keyring() Roberto Sassu
@ 2024-09-13  4:45 ` Herbert Xu
  2024-09-13  8:30   ` Roberto Sassu
                     ` (2 more replies)
  14 siblings, 3 replies; 37+ messages in thread
From: Herbert Xu @ 2024-09-13  4:45 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: dhowells, dwmw2, davem, linux-kernel, keyrings, linux-crypto,
	zohar, linux-integrity, torvalds, roberto.sassu

Roberto Sassu <roberto.sassu@huaweicloud.com> wrote:
>
> For the envisioned use cases, PGP operations cannot be done in user space,
> since the consumers are in the kernel itself (Integrity Digest Cache and
> IMA). Also they cannot be done in a trusted initial ram disk, since PGP
> operations can occur also while the system is running (e.g. after software
> package installation).

Does this address Linus's objections? If not then we cannot proceed.

Personally I don't think the argument above holds water.  With
IPsec we had a similar issue of authenticating untrusted peers
using public key cryptography.  In that case we successfully
delegated the task to user-space and it is still how it works
to this day.

A user-space daemon dedicated to public key crypto seems equally
applicable to your scenario.

The original application that brought public key crypto into the
kernel was module loading.  If we really wanted to we could extend
the user-space verification to modules too and perhaps kick all
public key crypto out of the kernel.

The complexity and lack of reviewer attention in this area means
that we're more likely to introduce security holes into the kernel
with such code.

Cheers,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-13  4:45 ` [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Herbert Xu
@ 2024-09-13  8:30   ` Roberto Sassu
  2024-09-13  9:00     ` Herbert Xu
  2024-09-15  7:11     ` Linus Torvalds
  2024-09-13  9:32   ` David Howells
  2024-09-14 11:29   ` Jarkko Sakkinen
  2 siblings, 2 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-13  8:30 UTC (permalink / raw)
  To: Herbert Xu
  Cc: dhowells, dwmw2, davem, linux-kernel, keyrings, linux-crypto,
	zohar, linux-integrity, torvalds, roberto.sassu,
	linux-security-module

On Fri, 2024-09-13 at 12:45 +0800, Herbert Xu wrote:
> Roberto Sassu <roberto.sassu@huaweicloud.com> wrote:

+ linux-security-module

> > 
> > For the envisioned use cases, PGP operations cannot be done in user space,
> > since the consumers are in the kernel itself (Integrity Digest Cache and
> > IMA). Also they cannot be done in a trusted initial ram disk, since PGP
> > operations can occur also while the system is running (e.g. after software
> > package installation).
> 
> Does this address Linus's objections? If not then we cannot proceed.

I hope to get an answer from him.

> Personally I don't think the argument above holds water.  With
> IPsec we had a similar issue of authenticating untrusted peers
> using public key cryptography.  In that case we successfully
> delegated the task to user-space and it is still how it works
> to this day.

That makes sense, since it is not the kernel holding secrets on behalf
of user space, it is user space passing the crypto material to the
kernel (if I remember IPSEC correctly). Failure of user space to hold
its secrets or to tamper with the task has only effect in user space.

With my understanding, I'm citing a source enumerating the requirements
of a secure system:

James P. Anderson: Computer Security Technology Planning Study

The first requirement of a component enforcing a security policy on a
Target of Evaluation (TOE), aka the reference monitor, is that it must
be tamperproof [1].

The security policy I want to enforce is: all code that the system
executes has been built by a trusted source (e.g. a Linux
distribution).

I want to leverage the kernel to enforce such security policy, and I
assume that the kernel can be fortified enough (for example through the
lockdown LSM) to be considered tamperproof against the TOE (the user
space processes).

The first problem I see in delegating the public crypto task to user
space is that it is at the same time part of the reference monitor
(since it is used to enforce the security policy) and it is a TOE too.

The second problem is, assuming that the task is verified through other
means other than PGP (but again, we are still relying on the public
crypto functionality to be performed by the kernel, for this to work),
that I didn't get a confirmation that user space can have equivalent
isolation guarantees as the kernel:

https://lore.kernel.org/linux-integrity/eb31920bd00e2c921b0aa6ebed8745cb0130b0e1.camel@huaweicloud.com/


Please, keep in mind that I already proposed what you suggested:

https://lore.kernel.org/linux-kernel/20230317145240.363908-1-roberto.sassu@huaweicloud.com/#r


After discussing with some kernel developers, the outcome was that a
better choice would be to put the code in the kernel, if I want
reasonable tamperproof guarantees.

Thanks

Roberto


[1] https://seclab.cs.ucdavis.edu/projects/history/papers/ande72a.pdf (page 17)

> A user-space daemon dedicated to public key crypto seems equally
> applicable to your scenario.
> 
> The original application that brought public key crypto into the
> kernel was module loading.  If we really wanted to we could extend
> the user-space verification to modules too and perhaps kick all
> public key crypto out of the kernel.
> 
> The complexity and lack of reviewer attention in this area means
> that we're more likely to introduce security holes into the kernel
> with such code.
> 
> Cheers,


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-13  8:30   ` Roberto Sassu
@ 2024-09-13  9:00     ` Herbert Xu
  2024-09-15  7:11     ` Linus Torvalds
  1 sibling, 0 replies; 37+ messages in thread
From: Herbert Xu @ 2024-09-13  9:00 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: dhowells, dwmw2, davem, linux-kernel, keyrings, linux-crypto,
	zohar, linux-integrity, torvalds, roberto.sassu,
	linux-security-module

On Fri, Sep 13, 2024 at 10:30:11AM +0200, Roberto Sassu wrote:
>
> The second problem is, assuming that the task is verified through other
> means other than PGP (but again, we are still relying on the public
> crypto functionality to be performed by the kernel, for this to work),
> that I didn't get a confirmation that user space can have equivalent
> isolation guarantees as the kernel:
> 
> https://lore.kernel.org/linux-integrity/eb31920bd00e2c921b0aa6ebed8745cb0130b0e1.camel@huaweicloud.com/
> 
> 
> Please, keep in mind that I already proposed what you suggested:
> 
> https://lore.kernel.org/linux-kernel/20230317145240.363908-1-roberto.sassu@huaweicloud.com/#r
> 
> 
> After discussing with some kernel developers, the outcome was that a
> better choice would be to put the code in the kernel, if I want
> reasonable tamperproof guarantees.

Where is this discussion? I clicked through the two links above
and everyone seems to agree that putting it in user-space is a good
idea.

Thanks,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-13  4:45 ` [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Herbert Xu
  2024-09-13  8:30   ` Roberto Sassu
@ 2024-09-13  9:32   ` David Howells
  2024-09-13 10:46     ` Ard Biesheuvel
  2024-09-14 11:29   ` Jarkko Sakkinen
  2 siblings, 1 reply; 37+ messages in thread
From: David Howells @ 2024-09-13  9:32 UTC (permalink / raw)
  To: Herbert Xu
  Cc: dhowells, Roberto Sassu, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, torvalds, roberto.sassu

Herbert Xu <herbert@gondor.apana.org.au> wrote:

> Personally I don't think the argument above holds water.  With
> IPsec we had a similar issue of authenticating untrusted peers
> using public key cryptography.  In that case we successfully
> delegated the task to user-space and it is still how it works
> to this day.

It transpires that we do actually need at least a PGP parser in the kernel -
and it needs to be used prior to loading any modules: some Lenovo Thinkpads,
at least, may have EFI variables holding a list of keys in PGP form, not X.509
form.

For example, in dmesg, you might see:

May 16 04:01:01 localhost kernel: integrity: Loading X.509 certificate: UEFI:MokListRT (MOKvar table)
May 16 04:01:01 localhost kernel: integrity: Problem loading X.509 certificate -126

On my laptop, if I dump this variable:

	efivar -e /tmp/q --name=605dab50-e046-4300-abb6-3dd810dd8b23-MokListRT

And then looking at the data exported:

	file /tmp/q

I see:

	/tmp/q: PGP Secret Sub-key -

The kernel doesn't currently have a PGP parser.  I've checked and the value
doesn't parse as ASN.1:

	openssl asn1parse -in /tmp/q -inform DER
	    0:d=0  hl=2 l=  21 prim: cont [ 23 ]       
	Error in encoding
	001EBA93B67F0000:error:0680007B:asn1 encoding routines:ASN1_get_object:header too long:crypto/asn1/asn1_lib.c:105:

which would suggest that it isn't X.509.

David


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-13  9:32   ` David Howells
@ 2024-09-13 10:46     ` Ard Biesheuvel
  0 siblings, 0 replies; 37+ messages in thread
From: Ard Biesheuvel @ 2024-09-13 10:46 UTC (permalink / raw)
  To: David Howells
  Cc: Herbert Xu, Roberto Sassu, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, torvalds, roberto.sassu

On Fri, 13 Sept 2024 at 11:32, David Howells <dhowells@redhat.com> wrote:
>
> Herbert Xu <herbert@gondor.apana.org.au> wrote:
>
> > Personally I don't think the argument above holds water.  With
> > IPsec we had a similar issue of authenticating untrusted peers
> > using public key cryptography.  In that case we successfully
> > delegated the task to user-space and it is still how it works
> > to this day.

This is slightly disingenuous. The kernel itself has no need to trust
the peer - it only manages the network pipe and authenticates/decrypts
the packets on behalf of user space.

The situation would be radically different if the kernel itself would
communicate over IPsec (or HTTPS) directly.

Likewise for module loading: there is no way the authentication can be
delegated to user space, unless that user space component is
authenticated by the kernel (and runs in a special, hardened context).
>
> It transpires that we do actually need at least a PGP parser in the kernel -
> and it needs to be used prior to loading any modules: some Lenovo Thinkpads,
> at least, may have EFI variables holding a list of keys in PGP form, not X.509
> form.
>
> For example, in dmesg, you might see:
>
> May 16 04:01:01 localhost kernel: integrity: Loading X.509 certificate: UEFI:MokListRT (MOKvar table)
> May 16 04:01:01 localhost kernel: integrity: Problem loading X.509 certificate -126
>

MokListRT is a shim construct, which is a component in the downstream
distro boot chain. It is not part of EFI, and in your case, this is
unlikely to be specific to Lenovo systems.

> On my laptop, if I dump this variable:
>
>         efivar -e /tmp/q --name=605dab50-e046-4300-abb6-3dd810dd8b23-MokListRT
>
> And then looking at the data exported:
>
>         file /tmp/q
>
> I see:
>
>         /tmp/q: PGP Secret Sub-key -
>
> The kernel doesn't currently have a PGP parser.  I've checked and the value
> doesn't parse as ASN.1:
>
>         openssl asn1parse -in /tmp/q -inform DER
>             0:d=0  hl=2 l=  21 prim: cont [ 23 ]
>         Error in encoding
>         001EBA93B67F0000:error:0680007B:asn1 encoding routines:ASN1_get_object:header too long:crypto/asn1/asn1_lib.c:105:
>
> which would suggest that it isn't X.509.
>

This should be fixed in shim not the kernel.

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-13  4:45 ` [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Herbert Xu
  2024-09-13  8:30   ` Roberto Sassu
  2024-09-13  9:32   ` David Howells
@ 2024-09-14 11:29   ` Jarkko Sakkinen
  2 siblings, 0 replies; 37+ messages in thread
From: Jarkko Sakkinen @ 2024-09-14 11:29 UTC (permalink / raw)
  To: Herbert Xu, Roberto Sassu
  Cc: dhowells, dwmw2, davem, linux-kernel, keyrings, linux-crypto,
	zohar, linux-integrity, torvalds, roberto.sassu

On Fri Sep 13, 2024 at 7:45 AM EEST, Herbert Xu wrote:
> Roberto Sassu <roberto.sassu@huaweicloud.com> wrote:
> >
> > For the envisioned use cases, PGP operations cannot be done in user space,
> > since the consumers are in the kernel itself (Integrity Digest Cache and
> > IMA). Also they cannot be done in a trusted initial ram disk, since PGP
> > operations can occur also while the system is running (e.g. after software
> > package installation).
>
> Does this address Linus's objections? If not then we cannot proceed.

I don't get why integrity digest cache is brought up in patch set
discussion in the first place. It is RFC patch set. It is misleading
to bring up everywhere as it was something we would need to take in
the account.

Let's worry about that once it is even a feature.

BR, Jarkko

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-13  8:30   ` Roberto Sassu
  2024-09-13  9:00     ` Herbert Xu
@ 2024-09-15  7:11     ` Linus Torvalds
  2024-09-15  8:07       ` Herbert Xu
  2024-09-15 10:51       ` Roberto Sassu
  1 sibling, 2 replies; 37+ messages in thread
From: Linus Torvalds @ 2024-09-15  7:11 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: Herbert Xu, dhowells, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module

On Fri, 13 Sept 2024 at 10:30, Roberto Sassu
<roberto.sassu@huaweicloud.com> wrote:
>
> On Fri, 2024-09-13 at 12:45 +0800, Herbert Xu wrote:
> >
> > Does this address Linus's objections? If not then we cannot proceed.
>
> I hope to get an answer from him.

So honestly, just the series adding pgp key verification I have no
objection to. The use case where some firmware uses pgp to validate
allowed keys in EFI variables etc sounds like a "ok, then we need to
parse them".

The objections I had were against the whole "start doing policy in
kernel", with what sounded like actually parsing and unpacking rpm
contents and verifying them with a pgp key. *That* still sounds like a
disaster to me, and is the part that made me go "why isn't that done
in user space together with then generating the fsverifty
information"?

The argument that the kernel is the only part of the system you trust
is bogus. The kernel does nothing on its own (apart from device
enumeration etc of course), so if you have no trustworthy user space,
then you might as well just give up entirely. At a *minimum* you have
initrd, and that can then be the start of a chain of user space trust.

Parsing rpm files in the kernel really sounds horrendous. But that
doesn't mean that I hate *this* series that just adds pgp key handling
in case there are other valid uses for it.

But maybe I misunderstood the original suggestion from Roberto.

              Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15  7:11     ` Linus Torvalds
@ 2024-09-15  8:07       ` Herbert Xu
  2024-09-15  8:40         ` Linus Torvalds
  2024-09-15 10:51       ` Roberto Sassu
  1 sibling, 1 reply; 37+ messages in thread
From: Herbert Xu @ 2024-09-15  8:07 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Roberto Sassu, dhowells, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module, Ard Biesheuvel

On Sun, Sep 15, 2024 at 09:11:04AM +0200, Linus Torvalds wrote:
>
> So honestly, just the series adding pgp key verification I have no
> objection to. The use case where some firmware uses pgp to validate
> allowed keys in EFI variables etc sounds like a "ok, then we need to
> parse them".

The use-case for EFI variables appears to be invalid:

https://lore.kernel.org/all/CAMj1kXH8nWtAzX+9xc2tLyy5d0w==JNQCMJBAbL=LdcF+XrYkw@mail.gmail.com/

> The objections I had were against the whole "start doing policy in
> kernel", with what sounded like actually parsing and unpacking rpm
> contents and verifying them with a pgp key. *That* still sounds like a
> disaster to me, and is the part that made me go "why isn't that done
> in user space together with then generating the fsverifty
> information"?

If the aformentioned EFI use-case is bogus, then distro package
verification is going to be the only application for PGP keys in
the kernel.  

Cheers,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15  8:07       ` Herbert Xu
@ 2024-09-15  8:40         ` Linus Torvalds
  2024-09-15  9:15           ` Herbert Xu
  2024-09-26  9:41           ` Roberto Sassu
  0 siblings, 2 replies; 37+ messages in thread
From: Linus Torvalds @ 2024-09-15  8:40 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Roberto Sassu, dhowells, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module, Ard Biesheuvel

On Sun, 15 Sept 2024 at 10:08, Herbert Xu <herbert@gondor.apana.org.au> wrote:
>
> If the aformentioned EFI use-case is bogus, then distro package
> verification is going to be the only application for PGP keys in
> the kernel.

So I haven't actually seen _that_ series, but as mentioned it does
smell pretty conceptually broken to me.

But hey, code talks, bullshit walks. People can most certainly try to
convince me.

                   Linus

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15  8:40         ` Linus Torvalds
@ 2024-09-15  9:15           ` Herbert Xu
  2024-09-15  9:31             ` Herbert Xu
  2024-09-26  9:41           ` Roberto Sassu
  1 sibling, 1 reply; 37+ messages in thread
From: Herbert Xu @ 2024-09-15  9:15 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Roberto Sassu, dhowells, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module, Ard Biesheuvel

On Sun, Sep 15, 2024 at 10:40:15AM +0200, Linus Torvalds wrote:
> 
> So I haven't actually seen _that_ series, but as mentioned it does
> smell pretty conceptually broken to me.
> 
> But hey, code talks, bullshit walks. People can most certainly try to
> convince me.

Roberto, correct me if I'm wrong but your intended use case is
the following patch series, right?

https://lore.kernel.org/linux-integrity/20240905152512.3781098-1-roberto.sassu@huaweicloud.com/

Thanks,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15  9:15           ` Herbert Xu
@ 2024-09-15  9:31             ` Herbert Xu
  2024-09-15 17:52               ` Roberto Sassu
  0 siblings, 1 reply; 37+ messages in thread
From: Herbert Xu @ 2024-09-15  9:31 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Roberto Sassu, dhowells, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module, Ard Biesheuvel

On Sun, Sep 15, 2024 at 05:15:25PM +0800, Herbert Xu wrote:
>
> Roberto, correct me if I'm wrong but your intended use case is
> the following patch series, right?

Actually the meat of the changes is in the following series:

https://lore.kernel.org/linux-integrity/20240905150543.3766895-1-roberto.sassu@huaweicloud.com/

Cheers,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15  7:11     ` Linus Torvalds
  2024-09-15  8:07       ` Herbert Xu
@ 2024-09-15 10:51       ` Roberto Sassu
  1 sibling, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-15 10:51 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Herbert Xu, dhowells, dwmw2, davem, linux-kernel, keyrings,
	linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module

On 9/15/2024 9:11 AM, Linus Torvalds wrote:
> On Fri, 13 Sept 2024 at 10:30, Roberto Sassu

[...]

> The objections I had were against the whole "start doing policy in
> kernel", with what sounded like actually parsing and unpacking rpm
> contents and verifying them with a pgp key. *That* still sounds like a
> disaster to me, and is the part that made me go "why isn't that done
> in user space together with then generating the fsverifty
> information"?

In my opinion, trusting root in this situation is not ideal. Trusting 
root means trusting all applications that root can run, that they will 
verify PGP signatures of fsverity digests with Linux distribution keys. 
In order to trust them, we would need to check the integrity of all 
those applications, in particular file read and IPC with the rest of the 
system.

A safer way to achieve the same goal is to let the kernel verify PGP 
signatures, assuming that the kernel is more privileged and cannot be 
tampered by root (for example, by using the 'lockdown' LSM). Since the 
PGP keys of the Linux distribution would be embedded in the kernel image 
(or certified by the embedded ones), trusting the system would require 
only to verify the kernel image itself (for example, with the boot loader).

Kernel-based policy enforcement is currently adopted by other LSMs, such 
as SELinux. SELinux also parses and enforces a policy sent from user 
space in the kernel. This does not mean that the policy itself is in the 
kernel, but that the kernel is the only component in the position of 
enforcing the policy without trusting all applications that root can run.

Roberto

> The argument that the kernel is the only part of the system you trust
> is bogus. The kernel does nothing on its own (apart from device
> enumeration etc of course), so if you have no trustworthy user space,
> then you might as well just give up entirely. At a *minimum* you have
> initrd, and that can then be the start of a chain of user space trust.
> 
> Parsing rpm files in the kernel really sounds horrendous. But that
> doesn't mean that I hate *this* series that just adds pgp key handling
> in case there are other valid uses for it.
> 
> But maybe I misunderstood the original suggestion from Roberto.
> 
>                Linus


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15  9:31             ` Herbert Xu
@ 2024-09-15 17:52               ` Roberto Sassu
  2024-09-17 11:27                 ` Dr. Greg
  0 siblings, 1 reply; 37+ messages in thread
From: Roberto Sassu @ 2024-09-15 17:52 UTC (permalink / raw)
  To: Herbert Xu, Linus Torvalds
  Cc: dhowells, dwmw2, davem, linux-kernel, keyrings, linux-crypto,
	zohar, linux-integrity, roberto.sassu, linux-security-module,
	Ard Biesheuvel

On 9/15/2024 11:31 AM, Herbert Xu wrote:
> On Sun, Sep 15, 2024 at 05:15:25PM +0800, Herbert Xu wrote:
>>
>> Roberto, correct me if I'm wrong but your intended use case is
>> the following patch series, right?
> 
> Actually the meat of the changes is in the following series:
> 
> https://lore.kernel.org/linux-integrity/20240905150543.3766895-1-roberto.sassu@huaweicloud.com/

Yes, correct. The idea is to verify the authenticity of RPM headers, 
extract the file digests from them, and use those file digests as 
reference values for integrity checking of files accessed by user space 
processes.

If the calculated digest of a file being accessed matches one extracted 
from the RPM header, access is granted otherwise it is denied.

Roberto


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15 17:52               ` Roberto Sassu
@ 2024-09-17 11:27                 ` Dr. Greg
  0 siblings, 0 replies; 37+ messages in thread
From: Dr. Greg @ 2024-09-17 11:27 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: Herbert Xu, Linus Torvalds, dhowells, dwmw2, davem, linux-kernel,
	keyrings, linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module, Ard Biesheuvel

On Sun, Sep 15, 2024 at 07:52:13PM +0200, Roberto Sassu wrote:

Good morning, I hope the day is starting well for everyone.

> On 9/15/2024 11:31 AM, Herbert Xu wrote:
> >On Sun, Sep 15, 2024 at 05:15:25PM +0800, Herbert Xu wrote:
> >>
> >>Roberto, correct me if I'm wrong but your intended use case is
> >>the following patch series, right?
> >
> >Actually the meat of the changes is in the following series:
> >
> >https://lore.kernel.org/linux-integrity/20240905150543.3766895-1-roberto.sassu@huaweicloud.com/

> Yes, correct. The idea is to verify the authenticity of RPM headers,
> extract the file digests from them, and use those file digests as
> reference values for integrity checking of files accessed by user
> space processes.
>
> If the calculated digest of a file being accessed matches one
> extracted from the RPM header, access is granted otherwise it is
> denied.

Based on the above response and your comment:

"The security policy I want to enforce is: all code that the system
executes has been built by a trusted source (e.g. a Linux
distribution)."

From the following URL:

https://lore.kernel.org/linux-integrity/b4a3e55650a9e9f2302cf093e5cc8e739b4ac98f.camel@huaweicloud.com/#t

What you are advocating for then, with this patch series and the
digest cache series, is a security policy that requires signed code
execution, correct?

Nothing wrong with that, it is a reasonable security desire, but it
seems wrong to conflate that with the implementation of the digest
cache.  There is a great deal of utility in a digest cache but it
doesn't require the need to parse RPM header information and/or TLV
sequences in the kernel.

That would only appear to be a requirement if your goal is a signed
executable policy that is implemented through a packaging medium,
correct?

To wit:

If I have security infrastructure that gives me confidence in the
integrity of the files on my media, I can populate a digest cache with
a simple ASCII list of pathnames fed into the kernel at boot time.

If I don't have confidence in the integrity of the files on my media I
could append a known good checksum to each pathname with the last
entry in the list being a PGP signature over the input stream.

I brought the following issue up in the patch series that Herbert
links to above, but will do so here, since I believe it has relevance
to this conversation as well.

If the goal is to have the digest cache be relevant from an integrity
perspective, particularly a signed code policy, you have to physically
read every file that has a digest value in the RPM digest list.
Otherwise the scheme is vulnerable to a Time Of Measurement Time Of
Use (TOMTOU) vulnerability scenario, correct?

This requires that one needs to experience a latency hit at least
once, presumably at boot when you prime the digest cache, correct?

An alternative approach may be to separate the RPM/TLV parsing code
from the digest code and implement RPM/Debian/whatever parsing in a
loadable module that would in turn populate the digest cache.

That may be a more acceptable strategy since it places the potential
security vulnerabilities of a parser into something that an entity
that is interested in a signed code policy would consider to be an
acceptable tradeoff from a security perspective.

> Roberto

Hopefully the above comments and clarifications will be helpful in
furthering additional discussion.

Have a good day.

As always,
Dr. Greg

The Quixote Project - Flailing at the Travails of Cybersecurity
              https://github.com/Quixote-Project

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-15  8:40         ` Linus Torvalds
  2024-09-15  9:15           ` Herbert Xu
@ 2024-09-26  9:41           ` Roberto Sassu
  2024-09-27  1:25             ` Dr. Greg
  2024-10-04 10:42             ` Roberto Sassu
  1 sibling, 2 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-09-26  9:41 UTC (permalink / raw)
  To: Linus Torvalds, Herbert Xu
  Cc: dhowells, dwmw2, davem, linux-kernel, keyrings, linux-crypto,
	zohar, linux-integrity, roberto.sassu, linux-security-module,
	Ard Biesheuvel

On Sun, 2024-09-15 at 10:40 +0200, Linus Torvalds wrote:
> On Sun, 15 Sept 2024 at 10:08, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> > 
> > If the aformentioned EFI use-case is bogus, then distro package
> > verification is going to be the only application for PGP keys in
> > the kernel.
> 
> So I haven't actually seen _that_ series, but as mentioned it does
> smell pretty conceptually broken to me.
> 
> But hey, code talks, bullshit walks. People can most certainly try to
> convince me.

The solution has three parts.

1. The kernel verifies the RPM header with a PGP key embedded in the
kernel, and provided by the Linux distribution vendor.

2. The Integrity Digest Cache parses the verified RPM header in the
kernel and feeds one of the existing LSMs (IMA, IPE and BPF LSM) with
the digests extracted from the RPM header.

3. The LSMs compare the fsverity digest they find in the filesystem
with the authenticated ones from the RPM header, and might deny access
to the file if the digests don't match.

At this point, RPM headers don't contain fsverity digests, only file
content digests, but this is an orthogonal problem.


I had a look at previous threads on similar topics, to find your
position on the matter.

I got that you would not be probably against (1), and maybe not (3).

However, we still need a source telling whether the fsverity digest in
the filesystem is the same of one calculated by Linux distributions
during build. That is what the Integrity Digest Cache provides.

Regarding (2), maybe I'm missing something fundamental, but isn't
parsing the ELF format of kernel modules from the kernel similar?

Cannot really go to user space at this point, since the authenticated
fsverity digests are directly consumed by LSMs. Also, as David pointed
out in this thread [1], there is no obligation for user space to call
any signature verification function before executing a file, this task
must be done by an LSM.

I'm aware that we should not run unnecessary code in the kernel. I
tried to mitigate this issue by striping the parsing functionality to
the minimum (220 LOC), and formally verifying it with the Frama-C
static analyzer. The parser is available here [2].

I'm also aware that this is not the long term solution, but I didn't
find much support on the alternatives, like a trustworthy user mode
driver [3][4] (isolated from other root processes) and signed eBPF
programs [5].

What it would be the right way to proceed, in your opinion?

Thanks

Roberto

[1] https://lore.kernel.org/linux-kernel/32081.1171560770@redhat.com/
[2] https://lore.kernel.org/linux-integrity/20240905150543.3766895-9-roberto.sassu@huaweicloud.com/
[3] https://lore.kernel.org/lkml/20230317145240.363908-1-roberto.sassu@huaweicloud.com/#t
[4] https://lore.kernel.org/linux-integrity/eb31920bd00e2c921b0aa6ebed8745cb0130b0e1.camel@huaweicloud.com/
[5] https://lwn.net/Articles/853489/


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-26  9:41           ` Roberto Sassu
@ 2024-09-27  1:25             ` Dr. Greg
  2024-10-04 10:42             ` Roberto Sassu
  1 sibling, 0 replies; 37+ messages in thread
From: Dr. Greg @ 2024-09-27  1:25 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: Linus Torvalds, Herbert Xu, dhowells, dwmw2, davem, linux-kernel,
	keyrings, linux-crypto, zohar, linux-integrity, roberto.sassu,
	linux-security-module, Ard Biesheuvel

On Thu, Sep 26, 2024 at 11:41:51AM +0200, Roberto Sassu wrote:

Good evening, I hope the week has gone well for everyone.

> On Sun, 2024-09-15 at 10:40 +0200, Linus Torvalds wrote:
> > On Sun, 15 Sept 2024 at 10:08, Herbert Xu <herbert@gondor.apana.org.au> wrote:
> > > 
> > > If the aformentioned EFI use-case is bogus, then distro package
> > > verification is going to be the only application for PGP keys in
> > > the kernel.
> > 
> > So I haven't actually seen _that_ series, but as mentioned it does
> > smell pretty conceptually broken to me.
> > 
> > But hey, code talks, bullshit walks. People can most certainly try to
> > convince me.

> The solution has three parts.
> 
> 1. The kernel verifies the RPM header with a PGP key embedded in the
> kernel, and provided by the Linux distribution vendor.
> 
> 2. The Integrity Digest Cache parses the verified RPM header in the
> kernel and feeds one of the existing LSMs (IMA, IPE and BPF LSM) with
> the digests extracted from the RPM header.
> 
> 3. The LSMs compare the fsverity digest they find in the filesystem
> with the authenticated ones from the RPM header, and might deny access
> to the file if the digests don't match.
> 
> At this point, RPM headers don't contain fsverity digests, only file
> content digests, but this is an orthogonal problem.

So from the above, can it be assumed that the RPM parsing code isn't
useful until the RPM packages contain fsverity root hashes?

In addition, and we mentioned this previously in this thread, it seems
that one needs to 'eat' a full read of a file, at least once, in order
to generate an fsverity digest that is consistent with the actual
on-disk contents of the file.

So, once again, the notion of the implementation of a generic digest
cache would seem to be orthogonal to the issue of verifying that the
digest values in the cache are from a 'known good' source.

> I had a look at previous threads on similar topics, to find your
> position on the matter.
> 
> I got that you would not be probably against (1), and maybe not (3).
> 
> However, we still need a source telling whether the fsverity digest in
> the filesystem is the same of one calculated by Linux distributions
> during build. That is what the Integrity Digest Cache provides.
> 
> Regarding (2), maybe I'm missing something fundamental, but isn't
> parsing the ELF format of kernel modules from the kernel similar?
> 
> Cannot really go to user space at this point, since the authenticated
> fsverity digests are directly consumed by LSMs. Also, as David pointed
> out in this thread [1], there is no obligation for user space to call
> any signature verification function before executing a file, this task
> must be done by an LSM.
> 
> I'm aware that we should not run unnecessary code in the kernel. I
> tried to mitigate this issue by striping the parsing functionality to
> the minimum (220 LOC), and formally verifying it with the Frama-C
> static analyzer. The parser is available here [2].
> 
> I'm also aware that this is not the long term solution, but I didn't
> find much support on the alternatives, like a trustworthy user mode
> driver [3][4] (isolated from other root processes) and signed eBPF
> programs [5].

We mentioned this previously in the related threads you cite, our TSEM
LSM implementation allows the kernel to determine whether or not a
userspace process or the entirety of userspace should be 'trusted' at
any point in time.

The security footprint/model of a digest 'priming' workload is going
to be extremely small.  If the priming workload is invoked early in
the boot process the kernel can have assurance that neither the
priming workload or the surrounding userspace has commited any actions
that would be considered to make the system untrusted.

> What it would be the right way to proceed, in your opinion?

Alternatively, we had suggested previously that the RPM parsing code
may be a good candidate for implementation with a separate kernel
loadable module.  That places the parsing code in the kernel to meet
your security requirement and there is standardized infrastructure for
module signing that would ensure the integrity of the parser.

The module surfaces a pseudo-file through securityfs that userspace
can use to feed the RPM header into the kernel to drive the population
of the digest cache.  The parser only needs to be resident and
operative long enough to initialize the digest cache of a trusted
system and a kernel loadable module would seem to be consistent with
that model.

Once again, priming the digest cache with known good digests seems to
be an issue orthogonal to the implementation of the digest cache
itself.

We would be interested in knowing if we are misunderstanding the model
you are trying to achieve.

> Thanks
> 
> Roberto
> 
> [1] https://lore.kernel.org/linux-kernel/32081.1171560770@redhat.com/
> [2] https://lore.kernel.org/linux-integrity/20240905150543.3766895-9-roberto.sassu@huaweicloud.com/
> [3] https://lore.kernel.org/lkml/20230317145240.363908-1-roberto.sassu@huaweicloud.com/#t
> [4] https://lore.kernel.org/linux-integrity/eb31920bd00e2c921b0aa6ebed8745cb0130b0e1.camel@huaweicloud.com/
> [5] https://lwn.net/Articles/853489/

Have a good weekend.

As always,
Dr. Greg

The Quixote Project - Flailing at the Travails of Cybersecurity
              https://github.com/Quixote-Project

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures
  2024-09-26  9:41           ` Roberto Sassu
  2024-09-27  1:25             ` Dr. Greg
@ 2024-10-04 10:42             ` Roberto Sassu
  1 sibling, 0 replies; 37+ messages in thread
From: Roberto Sassu @ 2024-10-04 10:42 UTC (permalink / raw)
  To: Linus Torvalds, Herbert Xu
  Cc: dhowells, dwmw2, davem, linux-kernel, keyrings, linux-crypto,
	zohar, linux-integrity, roberto.sassu, linux-security-module,
	Ard Biesheuvel

On 9/26/2024 11:41 AM, Roberto Sassu wrote:
> On Sun, 2024-09-15 at 10:40 +0200, Linus Torvalds wrote:
>> On Sun, 15 Sept 2024 at 10:08, Herbert Xu <herbert@gondor.apana.org.au> wrote:
>>>
>>> If the aformentioned EFI use-case is bogus, then distro package
>>> verification is going to be the only application for PGP keys in
>>> the kernel.
>>
>> So I haven't actually seen _that_ series, but as mentioned it does
>> smell pretty conceptually broken to me.
>>
>> But hey, code talks, bullshit walks. People can most certainly try to
>> convince me.
> 
> The solution has three parts.
> 
> 1. The kernel verifies the RPM header with a PGP key embedded in the
> kernel, and provided by the Linux distribution vendor.
> 
> 2. The Integrity Digest Cache parses the verified RPM header in the
> kernel and feeds one of the existing LSMs (IMA, IPE and BPF LSM) with
> the digests extracted from the RPM header.
> 
> 3. The LSMs compare the fsverity digest they find in the filesystem
> with the authenticated ones from the RPM header, and might deny access
> to the file if the digests don't match.
> 
> At this point, RPM headers don't contain fsverity digests, only file
> content digests, but this is an orthogonal problem.
> 
> 
> I had a look at previous threads on similar topics, to find your
> position on the matter.
> 
> I got that you would not be probably against (1), and maybe not (3).
> 
> However, we still need a source telling whether the fsverity digest in
> the filesystem is the same of one calculated by Linux distributions
> during build. That is what the Integrity Digest Cache provides.
> 
> Regarding (2), maybe I'm missing something fundamental, but isn't
> parsing the ELF format of kernel modules from the kernel similar?
> 
> Cannot really go to user space at this point, since the authenticated
> fsverity digests are directly consumed by LSMs. Also, as David pointed
> out in this thread [1], there is no obligation for user space to call
> any signature verification function before executing a file, this task
> must be done by an LSM.
> 
> I'm aware that we should not run unnecessary code in the kernel. I
> tried to mitigate this issue by striping the parsing functionality to
> the minimum (220 LOC), and formally verifying it with the Frama-C
> static analyzer. The parser is available here [2].
> 
> I'm also aware that this is not the long term solution, but I didn't
> find much support on the alternatives, like a trustworthy user mode
> driver [3][4] (isolated from other root processes) and signed eBPF
> programs [5].
> 
> What it would be the right way to proceed, in your opinion?

If I remove the parsers completely from the kernel, and attach them 
dynamically with eBPF, would you reconsider my patch set?

Thanks

Roberto


^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2024-10-04 11:01 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-11 12:28 [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Roberto Sassu
2024-09-11 12:28 ` [PATCH v3 01/14] mpi: Introduce mpi_key_length() Roberto Sassu
2024-09-12 13:26   ` Jarkko Sakkinen
2024-09-11 12:28 ` [PATCH v3 02/14] rsa: add parser of raw format Roberto Sassu
2024-09-12 13:33   ` Jarkko Sakkinen
2024-09-11 12:29 ` [PATCH v3 03/14] PGPLIB: PGP definitions (RFC 9580) Roberto Sassu
2024-09-12 13:54   ` Jarkko Sakkinen
2024-09-11 12:29 ` [PATCH v3 04/14] PGPLIB: Basic packet parser Roberto Sassu
2024-09-12 13:57   ` Jarkko Sakkinen
2024-09-11 12:29 ` [PATCH v3 05/14] PGPLIB: Signature parser Roberto Sassu
2024-09-12 13:58   ` Jarkko Sakkinen
2024-09-11 12:29 ` [PATCH v3 06/14] KEYS: PGP data parser Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 07/14] KEYS: Provide PGP key description autogeneration Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 08/14] KEYS: PGP-based public key signature verification Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 09/14] KEYS: Retry asym key search with partial ID in restrict_link_by_signature() Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 10/14] KEYS: Calculate key digest and get signature of the key Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 11/14] verification: introduce verify_pgp_signature() Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 12/14] PGP: Provide a key type for testing PGP signatures Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 13/14] KEYS: Provide a function to load keys from a PGP keyring blob Roberto Sassu
2024-09-11 12:29 ` [PATCH v3 14/14] KEYS: Introduce load_pgp_public_keyring() Roberto Sassu
2024-09-13  4:45 ` [PATCH v3 00/14] KEYS: Add support for PGP keys and signatures Herbert Xu
2024-09-13  8:30   ` Roberto Sassu
2024-09-13  9:00     ` Herbert Xu
2024-09-15  7:11     ` Linus Torvalds
2024-09-15  8:07       ` Herbert Xu
2024-09-15  8:40         ` Linus Torvalds
2024-09-15  9:15           ` Herbert Xu
2024-09-15  9:31             ` Herbert Xu
2024-09-15 17:52               ` Roberto Sassu
2024-09-17 11:27                 ` Dr. Greg
2024-09-26  9:41           ` Roberto Sassu
2024-09-27  1:25             ` Dr. Greg
2024-10-04 10:42             ` Roberto Sassu
2024-09-15 10:51       ` Roberto Sassu
2024-09-13  9:32   ` David Howells
2024-09-13 10:46     ` Ard Biesheuvel
2024-09-14 11:29   ` Jarkko Sakkinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).