linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC][PATCH 00/12] keys: add support for PGP keys and signatures
@ 2018-11-12 10:24 Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 01/12] mpi: introduce mpi_key_length() Roberto Sassu
                   ` (14 more replies)
  0 siblings, 15 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel,
	silviu.vlasceanu, Roberto Sassu

This patch set is based on kernel/git/dhowells/linux-modsign.git
(branch: pgp-parser) at git.kernel.org.

The goal of this patch set is to add support for PGP keys and signatures,
so that it will be possible to verify RPM header signatures (included in
RPM-based Linux distributions) when IMA Appraisal is enabled.

The patch set includes two preliminary patches: the first introduces
mpi_key_length(), to get the number of bits and bytes of an MPI; the second
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.

The remaining of the patch set includes the original patches with
modifications to work with the current kernel. It additionally introduces
verify_pgp_signature(), to verify PGP signatures with built-in or secondary
trusted keys. Trusted keys can be included in the kernel by enabling
CONFIG_PGP_PRELOAD_PUBLIC_KEYS and by copying the file pubring.gpg
containing the PGP keyring to the kernel source directory.

The changelog is included in the description of each patch.

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

Roberto Sassu (4):
  mpi: introduce mpi_key_length()
  rsa: add parser of raw format
  verification: introduce verify_pgp_signature()
  KEYS: Introduce load_pgp_public_keyring()

 certs/Kconfig                           |   7 +
 certs/Makefile                          |   3 +
 certs/system_keyring.c                  |  64 +++
 crypto/asymmetric_keys/Kconfig          |  38 ++
 crypto/asymmetric_keys/Makefile         |  15 +
 crypto/asymmetric_keys/pgp_library.c    | 625 ++++++++++++++++++++++++
 crypto/asymmetric_keys/pgp_parser.h     |  22 +
 crypto/asymmetric_keys/pgp_preload.c    | 118 +++++
 crypto/asymmetric_keys/pgp_public_key.c | 380 ++++++++++++++
 crypto/asymmetric_keys/pgp_signature.c  | 428 ++++++++++++++++
 crypto/asymmetric_keys/pgp_test_key.c   | 132 +++++
 crypto/rsa.c                            |  14 +-
 crypto/rsa_helper.c                     |  69 +++
 include/crypto/internal/rsa.h           |   6 +
 include/linux/mpi.h                     |   2 +
 include/linux/pgp.h                     | 215 ++++++++
 include/linux/pgp_sig.h                 |  24 +
 include/linux/pgplib.h                  |  87 ++++
 include/linux/verification.h            |   5 +
 lib/mpi/mpicoder.c                      |  33 +-
 20 files changed, 2276 insertions(+), 11 deletions(-)
 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 include/linux/pgp.h
 create mode 100644 include/linux/pgp_sig.h
 create mode 100644 include/linux/pgplib.h

-- 
2.17.1


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

* [RFC][PATCH 01/12] mpi: introduce mpi_key_length()
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 02/12] rsa: add parser of raw format Roberto Sassu
                   ` (13 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel,
	silviu.vlasceanu, Roberto Sassu

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

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/linux/mpi.h |  2 ++
 lib/mpi/mpicoder.c  | 33 ++++++++++++++++++++++++++-------
 2 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/include/linux/mpi.h b/include/linux/mpi.h
index 7cd1473c64a4..56187bb57c78 100644
--- a/include/linux/mpi.h
+++ b/include/linux/mpi.h
@@ -61,6 +61,8 @@ int mpi_resize(MPI a, unsigned nlimbs);
 
 /*-- mpicoder.c --*/
 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);
 MPI mpi_read_raw_from_sgl(struct scatterlist *sgl, unsigned int len);
 void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign);
diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c
index eead4b339466..207cb3b91a51 100644
--- a/lib/mpi/mpicoder.c
+++ b/lib/mpi/mpicoder.c
@@ -78,22 +78,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 *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.17.1


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

* [RFC][PATCH 02/12] rsa: add parser of raw format
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 01/12] mpi: introduce mpi_key_length() Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 03/12] PGPLIB: PGP definitions (RFC 4880) Roberto Sassu
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel,
	silviu.vlasceanu, Roberto Sassu

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

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/rsa.c                  | 14 +++++--
 crypto/rsa_helper.c           | 69 +++++++++++++++++++++++++++++++++++
 include/crypto/internal/rsa.h |  6 +++
 3 files changed, 85 insertions(+), 4 deletions(-)

diff --git a/crypto/rsa.c b/crypto/rsa.c
index 4167980c243d..d33aa039de6e 100644
--- a/crypto/rsa.c
+++ b/crypto/rsa.c
@@ -275,8 +275,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)
@@ -309,8 +312,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 efc78fe7ae2e..fda85fbe2e38 100644
--- a/crypto/rsa_helper.c
+++ b/crypto/rsa_helper.c
@@ -14,6 +14,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"
@@ -153,6 +154,32 @@ int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag,
 	return 0;
 }
 
+typedef int (*rsa_get_func)(void *, size_t, unsigned char,
+			    const void *, size_t);
+
+static int rsa_parse_key_raw(struct rsa_key *rsa_key,
+			     const void *key, unsigned int key_len,
+			     rsa_get_func *func, int n_func)
+{
+	unsigned int nbytes, len = key_len;
+	const void *key_ptr = key;
+	int ret, i;
+
+	for (i = 0; i < n_func; i++) {
+		ret = mpi_key_length(key_ptr, len, NULL, &nbytes);
+		if (ret < 0)
+			return ret;
+
+		ret = func[i](rsa_key, 0, 0, key_ptr + 2, nbytes);
+		if (ret < 0)
+			return ret;
+
+		key_ptr += nbytes + 2;
+	}
+
+	return (key_ptr == key + key_len) ? 0 : -EINVAL;
+}
+
 /**
  * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the
  *                       provided struct rsa_key, pointers to the raw key as is,
@@ -171,6 +198,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
@@ -189,3 +237,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 9e8f1590de98..6bc672daa175 100644
--- a/include/crypto/internal/rsa.h
+++ b/include/crypto/internal/rsa.h
@@ -55,8 +55,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.17.1


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

* [RFC][PATCH 03/12] PGPLIB: PGP definitions (RFC 4880)
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 01/12] mpi: introduce mpi_key_length() Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 02/12] rsa: add parser of raw format Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 04/12] PGPLIB: Basic packet parser Roberto Sassu
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

From: David Howells <dhowells@redhat.com>

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

Changelog:

v0:
- fix style issues (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/linux/pgp.h | 215 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 215 insertions(+)
 create mode 100644 include/linux/pgp.h

diff --git a/include/linux/pgp.h b/include/linux/pgp.h
new file mode 100644
index 000000000000..a01a0c219aa8
--- /dev/null
+++ b/include/linux/pgp.h
@@ -0,0 +1,215 @@
+/* PGP definitions (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_PGP_H
+#define _LINUX_PGP_H
+
+#include <linux/types.h>
+
+struct pgp_key_ID {
+	u8 id[8];
+} __packed;
+
+struct pgp_time {
+	u8 time[4];
+} __packed;
+
+/*
+ * PGP public-key algorithm identifiers [RFC4880: 9.1]
+ */
+enum pgp_pubkey_algo {
+	PGP_PUBKEY_RSA_ENC_OR_SIG	= 1,
+	PGP_PUBKEY_RSA_ENC_ONLY		= 2,
+	PGP_PUBKEY_RSA_SIG_ONLY		= 3,
+	PGP_PUBKEY_ELGAMAL		= 16,
+	PGP_PUBKEY_DSA			= 17,
+	PGP_PUBKEY__LAST
+};
+
+/*
+ * PGP symmetric-key algorithm identifiers [RFC4880: 9.2]
+ */
+enum pgp_symkey_algo {
+	PGP_SYMKEY_PLAINTEXT		= 0,
+	PGP_SYMKEY_IDEA			= 1,
+	PGP_SYMKEY_3DES			= 2,
+	PGP_SYMKEY_CAST5		= 3,
+	PGP_SYMKEY_BLOWFISH		= 4,
+	PGP_SYMKEY_AES_128KEY		= 7,
+	PGP_SYMKEY_AES_192KEY		= 8,
+	PGP_SYMKEY_AES_256KEY		= 9,
+	PGP_SYMKEY_TWOFISH_256KEY	= 10,
+};
+
+/*
+ * PGP compression algorithm identifiers [RFC4880: 9.3]
+ */
+enum pgp_compr_algo {
+	PGP_COMPR_UNCOMPRESSED		= 0,
+	PGP_COMPR_ZIP			= 1,
+	PGP_COMPR_ZLIB			= 2,
+	PGP_COMPR_BZIP2			= 3,
+};
+
+/*
+ * PGP hash algorithm identifiers [RFC4880: 9.4]
+ */
+enum pgp_hash_algo {
+	PGP_HASH_MD5			= 1,
+	PGP_HASH_SHA1			= 2,
+	PGP_HASH_RIPE_MD_160		= 3,
+	PGP_HASH_SHA256			= 8,
+	PGP_HASH_SHA384			= 9,
+	PGP_HASH_SHA512			= 10,
+	PGP_HASH_SHA224			= 11,
+	PGP_HASH__LAST
+};
+
+extern const char *const pgp_hash_algorithms[PGP_HASH__LAST];
+
+/*
+ * PGP packet type tags [RFC4880: 4.3].
+ */
+enum pgp_packet_tag {
+	PGP_PKT_RESERVED		= 0,
+	PGP_PKT_PUBKEY_ENC_SESSION_KEY	= 1,
+	PGP_PKT_SIGNATURE		= 2,
+	PGP_PKT_SYMKEY_ENC_SESSION_KEY	= 3,
+	PGP_PKT_ONEPASS_SIGNATURE	= 4,
+	PGP_PKT_SECRET_KEY		= 5,
+	PGP_PKT_PUBLIC_KEY		= 6,
+	PGP_PKT_SECRET_SUBKEY		= 7,
+	PGP_PKT_COMPRESSED_DATA		= 8,
+	PGP_PKT_SYM_ENC_DATA		= 9,
+	PGP_PKT_MARKER			= 10,
+	PGP_PKT_LITERAL_DATA		= 11,
+	PGP_PKT_TRUST			= 12,
+	PGP_PKT_USER_ID			= 13,
+	PGP_PKT_PUBLIC_SUBKEY		= 14,
+	PGP_PKT_USER_ATTRIBUTE		= 17,
+	PGP_PKT_SYM_ENC_AND_INTEG_DATA	= 18,
+	PGP_PKT_MODIFY_DETECT_CODE	= 19,
+	PGP_PKT_PRIVATE_0		= 60,
+	PGP_PKT_PRIVATE_3		= 63,
+	PGP_PKT__HIGHEST		= 63
+};
+
+/*
+ * Signature (tag 2) packet [RFC4880: 5.2].
+ */
+enum pgp_signature_version {
+	PGP_SIG_VERSION_3			= 3,
+	PGP_SIG_VERSION_4			= 4,
+};
+
+enum pgp_signature_type {
+	PGP_SIG_BINARY_DOCUMENT_SIG		= 0x00,
+	PGP_SIG_CANONICAL_TEXT_DOCUMENT_SIG	= 0x01,
+	PGP_SIG_STANDALONE_SIG			= 0x02,
+	PGP_SIG_GENERAL_CERT_OF_UID_PUBKEY	= 0x10,
+	PGP_SIG_PERSONAL_CERT_OF_UID_PUBKEY	= 0x11,
+	PGP_SIG_CASUAL_CERT_OF_UID_PUBKEY	= 0x12,
+	PGP_SIG_POSTITIVE_CERT_OF_UID_PUBKEY	= 0x13,
+	PGP_SIG_SUBKEY_BINDING_SIG		= 0x18,
+	PGP_SIG_PRIMARY_KEY_BINDING_SIG		= 0x19,
+	PGP_SIG_DIRECTLY_ON_KEY			= 0x1F,
+	PGP_SIG_KEY_REVOCATION_SIG		= 0x20,
+	PGP_SIG_SUBKEY_REVOCATION_SIG		= 0x28,
+	PGP_SIG_CERT_REVOCATION_SIG		= 0x30,
+	PGP_SIG_TIMESTAMP_SIG			= 0x40,
+	PGP_SIG_THIRD_PARTY_CONFIRM_SIG		= 0x50,
+};
+
+struct pgp_signature_v3_packet {
+	enum pgp_signature_version version : 8; /* == PGP_SIG_VERSION_3 */
+	u8	length_of_hashed;	/* == 5 */
+	struct {
+		enum pgp_signature_type signature_type : 8;
+		struct pgp_time	creation_time;
+	} __packed hashed;
+	struct pgp_key_ID 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 [RFC4880: 5.2.3.1].
+ */
+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__LAST
+};
+
+#define PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK	0x80
+
+/*
+ * Key (tag 5, 6, 7 and 14) packet
+ */
+enum pgp_key_version {
+	PGP_KEY_VERSION_2			= 2,
+	PGP_KEY_VERSION_3			= 3,
+	PGP_KEY_VERSION_4			= 4,
+};
+
+struct pgp_key_v3_packet {
+	enum pgp_key_version version : 8;
+	struct pgp_time	creation_time;
+	u8 expiry[2];			/* 0 or time in days till expiry */
+	enum pgp_pubkey_algo pubkey_algo : 8;
+	u8 key_material[0];
+} __packed;
+
+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[0];
+} __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,
+};
+
+#endif /* _LINUX_PGP_H */
-- 
2.17.1


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

* [RFC][PATCH 04/12] PGPLIB: Basic packet parser
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (2 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 03/12] PGPLIB: PGP definitions (RFC 4880) Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 05/12] PGPLIB: Signature parser Roberto Sassu
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

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.

Changelog

v0:
- fix style issues (Roberto Sassu)
- declare pgp_to_public_key_algo (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig       |   6 +
 crypto/asymmetric_keys/Makefile      |   5 +
 crypto/asymmetric_keys/pgp_library.c | 280 +++++++++++++++++++++++++++
 crypto/asymmetric_keys/pgp_parser.h  |  22 +++
 include/linux/pgplib.h               |  47 +++++
 5 files changed, 360 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp_library.c
 create mode 100644 crypto/asymmetric_keys/pgp_parser.h
 create mode 100644 include/linux/pgplib.h

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index be70ca6c85d3..38c36c14d712 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -93,4 +93,10 @@ config SIGNED_PE_FILE_VERIFICATION
 	  This option provides support for verifying the signature(s) on a
 	  signed PE binary.
 
+config PGP_LIBRARY
+	tristate "PGP parsing library"
+	help
+	  This option enables a library that provides a number of simple
+	  utility functions for parsing PGP (RFC 4880) packet-based messages.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 28b91adba2ae..55a67ebfe8e1 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -86,3 +86,8 @@ tpm_key_parser-y := \
 
 $(obj)/tpm_parser.o: $(obj)/tpm.asn1.h
 $(obj)/tpm.asn1.o: $(obj)/tpm.asn1.c $(obj)/tpm.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..0ab702ffe173
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -0,0 +1,280 @@
+/* PGP packet parser (RFC 4880)
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGPL: "fmt
+#include <linux/pgplib.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+MODULE_LICENSE("GPL");
+
+const char *const pgp_hash_algorithms[PGP_HASH__LAST] = {
+	[PGP_HASH_MD5]			= "md5",
+	[PGP_HASH_SHA1]			= "sha1",
+	[PGP_HASH_RIPE_MD_160]		= "rmd160",
+	[PGP_HASH_SHA256]		= "sha256",
+	[PGP_HASH_SHA384]		= "sha384",
+	[PGP_HASH_SHA512]		= "sha512",
+	[PGP_HASH_SHA224]		= "sha224",
+};
+EXPORT_SYMBOL_GPL(pgp_hash_algorithms);
+
+const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST] = {
+	[PGP_PUBKEY_RSA_ENC_OR_SIG]	= "rsa",
+	[PGP_PUBKEY_RSA_ENC_ONLY]	= "rsa",
+	[PGP_PUBKEY_RSA_SIG_ONLY]	= "rsa",
+	[PGP_PUBKEY_ELGAMAL]		= NULL,
+	[PGP_PUBKEY_DSA]		= NULL,
+};
+EXPORT_SYMBOL_GPL(pgp_to_public_key_algo);
+
+/**
+ * pgp_parse_packet_header - Parse a PGP packet header
+ * @_data: Start of the PGP packet (updated to PGP packet data)
+ * @_datalen: Amount of data remaining in buffer (decreased)
+ * @_type: Where the packet type will be returned
+ * @_headerlen: Where the header length will be returned
+ *
+ * Parse a set of PGP packet header [RFC 4880: 4.2].
+ *
+ * Returns 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", datalen, size);
+	if (datalen < size)
+		goto short_packet;
+	if ((int)size < 0)
+		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 4880: 4].
+ */
+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 4880: 5.5.2].
+ */
+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_2:
+	case PGP_KEY_VERSION_3:
+	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 */
+	} else {
+		unsigned short ndays;
+
+		ndays  = *data++ << 8;
+		ndays |= *data++;
+		if (ndays)
+			pk->expires_at = pk->creation_time + ndays * 86400UL;
+		else
+			pk->expires_at = 0;
+		datalen -= 2;
+	}
+
+	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/pgp_parser.h b/crypto/asymmetric_keys/pgp_parser.h
new file mode 100644
index 000000000000..5dfd68a88229
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_parser.h
@@ -0,0 +1,22 @@
+/* PGP crypto data parser internal definitions
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/pgp.h>
+
+#define kenter(FMT, ...) \
+	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+	pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+/*
+ * pgp_library.c
+ */
+extern const char *pgp_to_public_key_algo[PGP_PUBKEY__LAST];
diff --git a/include/linux/pgplib.h b/include/linux/pgplib.h
new file mode 100644
index 000000000000..7db21b69a6fe
--- /dev/null
+++ b/include/linux/pgplib.h
@@ -0,0 +1,47 @@
+/* PGP library definitions (RFC 4880)
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_PGPLIB_H
+#define _LINUX_PGPLIB_H
+
+#if IS_ENABLED(CONFIG_PGP_LIBRARY) || IS_ENABLED(CONFIG_PGP_LIBRARY_MODULE)
+
+#include <linux/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;
+	time_t		creation_time;
+	time_t		expires_at;
+};
+
+extern int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
+				struct pgp_parse_pubkey *pk);
+
+
+#endif /* CONFIG_PGP_LIBRARY */
+
+#endif /* _LINUX_PGPLIB_H */
-- 
2.17.1


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

* [RFC][PATCH 05/12] PGPLIB: Signature parser
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (3 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 04/12] PGPLIB: Basic packet parser Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 06/12] KEYS: PGP data parser Roberto Sassu
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

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

Changelog

v0:
- fix style issues (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/pgp_library.c | 283 ++++++++++++++++++++++++++-
 include/linux/pgplib.h               |  25 +++
 2 files changed, 307 insertions(+), 1 deletion(-)

diff --git a/crypto/asymmetric_keys/pgp_library.c b/crypto/asymmetric_keys/pgp_library.c
index 0ab702ffe173..13bfc9db1ae4 100644
--- a/crypto/asymmetric_keys/pgp_library.c
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -154,7 +154,7 @@ static ssize_t pgp_parse_packet_header(const u8 **_data, size_t *_datalen,
 		}
 	}
 
-	pr_devel("datalen=%zu size=%zu", datalen, size);
+	pr_devel("datalen=%zu size=%zu\n", datalen, size);
 	if (datalen < size)
 		goto short_packet;
 	if ((int)size < 0)
@@ -278,3 +278,284 @@ int pgp_parse_public_key(const u8 **_data, size_t *_datalen,
 	return 0;
 }
 EXPORT_SYMBOL_GPL(pgp_parse_public_key);
+
+/**
+ * pgp_parse_sig_subpkt_header - Parse a PGP V4 signature subpacket header
+ * @_data: Start of the subpacket (updated to subpacket data)
+ * @_datalen: Amount of data remaining in buffer (decreased)
+ * @_type: Where the subpacket type will be returned
+ *
+ * Parse a PGP V4 signature subpacket header [RFC 4880: 5.2.3.1].
+ *
+ * Returns 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 ((int)size < 0)
+		goto too_big;
+
+	type = *data++ & ~PGP_SIG_SUBPKT_TYPE_CRITICAL_MASK;
+	datalen--;
+	size--;
+
+	*_data = data;
+	*_datalen = datalen;
+	*_type = type;
+	pr_devel("Found subpkt type=%u size=%zd\n", type, size);
+	return size;
+
+very_short_subpacket:
+	pr_debug("Signature subpacket size can't be zero\n");
+	return -EBADMSG;
+short_subpacket:
+	pr_debug("Attempt to parse short signature subpacket\n");
+	return -EBADMSG;
+too_big:
+	pr_debug("Signature subpacket size >2G\n");
+	return -EMSGSIZE;
+}
+
+/**
+ * pgp_parse_sig_subpkts - Parse a set of PGP V4 signatute subpackets
+ * @_data: Data to be parsed (updated)
+ * @_datalen: Amount of data (updated)
+ * @ctx: Parsing context
+ *
+ * Parse a set of PGP signature subpackets [RFC 4880: 5.2.3].
+ */
+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 4880: 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.
+ *
+ * Returns 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/include/linux/pgplib.h b/include/linux/pgplib.h
index 7db21b69a6fe..44c8a07b32e3 100644
--- a/include/linux/pgplib.h
+++ b/include/linux/pgplib.h
@@ -41,6 +41,31 @@ 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);
+
 
 #endif /* CONFIG_PGP_LIBRARY */
 
-- 
2.17.1


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

* [RFC][PATCH 06/12] KEYS: PGP data parser
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (4 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 05/12] PGPLIB: Signature parser Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 07/12] KEYS: Provide PGP key description autogeneration Roberto Sassu
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

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 4880) and if it parses okay, attempts to extract a public-key
algorithm key or subkey from it.

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

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

Changelog

v0:
- remove declaration of pgp_to_public_key_algo (Roberto Sassu)
- replace prep->type_data with prep->payload.data (Roberto Sassu)
- replace public_key_destroy() with public_key_free() (Roberto Sassu)
- use asymmetric_key_generate_id() to generate the key identifiers
  (Roberto Sassu)
- add raw_fingerprint to pgp_key_data_parse_context structure
  (Roberto Sassu)
- replace algorithm identifiers with strings (Roberto Sassu)
- don't parse MPIs (Roberto Sassu)
- don't process Public-Subkey packets (only one key will be created)
  (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig          |  11 +
 crypto/asymmetric_keys/Makefile         |   4 +
 crypto/asymmetric_keys/pgp_public_key.c | 336 ++++++++++++++++++++++++
 3 files changed, 351 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp_public_key.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 38c36c14d712..356b85fc34bd 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -99,4 +99,15 @@ config PGP_LIBRARY
 	  This option enables a library that provides a number of simple
 	  utility functions for parsing PGP (RFC 4880) packet-based messages.
 
+config PGP_KEY_PARSER
+	tristate "PGP key parser"
+	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select PGP_LIBRARY
+	select MD5 # V3 fingerprint generation
+	select SHA1 # V4 fingerprint generation
+	help
+	  This option provides support for parsing PGP (RFC 4880) format blobs
+	  for key data and provides the ability to instantiate a crypto key
+	  from a public key packet found inside the blob.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 55a67ebfe8e1..a68f9a5d1746 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -91,3 +91,7 @@ $(obj)/tpm.asn1.o: $(obj)/tpm.asn1.c $(obj)/tpm.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_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c
new file mode 100644
index 000000000000..a5ce146a1250
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -0,0 +1,336 @@
+/* Instantiate a public key crypto key from PGP format data [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PGP: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mpi.h>
+#include <linux/pgplib.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
+
+MODULE_LICENSE("GPL");
+
+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;
+	unsigned char *raw_fingerprint;
+	char *fingerprint;
+};
+
+/*
+ * Calculate the public key ID (RFC4880 12.2)
+ */
+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;
+	int i, ret;
+
+	kenter("");
+
+	n = (pgp->version < PGP_KEY_VERSION_4) ? 8 : 6;
+	for (i = 0; i < MAX_MPI && keylen > 0; i++) {
+		ret = mpi_key_length(key_ptr, keylen, nb + i, nn + i);
+		if (ret < 0)
+			return ret;
+
+		pp[i] = key_ptr + 2;
+		key_ptr += 2 + nn[i];
+		keylen -= 2 + nn[i];
+		n += 2 + nn[i];
+	}
+
+	if (keylen != 0) {
+		pr_debug("excess %zu\n", keylen);
+		return -EBADMSG;
+	}
+
+	npkey = i;
+
+	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);
+
+	if (pgp->version < PGP_KEY_VERSION_4) {
+		u16 a16;
+
+		if (pgp->expires_at)
+			a16 = (pgp->expires_at - pgp->creation_time) / 86400UL;
+		else
+			a16 = 0;
+		digest_putc(digest, a16 >> 8);
+		digest_putc(digest, a16 >> 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;
+	u8 *raw_fingerprint;
+	int digest_size, offset;
+	int ret, i;
+
+	ret = -ENOMEM;
+	tfm = crypto_alloc_shash(pgp->version < PGP_KEY_VERSION_4 ?
+				 "md5" : "sha1", 0, 0);
+	if (!tfm)
+		goto cleanup;
+
+	digest = kmalloc(sizeof(*digest) + crypto_shash_descsize(tfm),
+			 GFP_KERNEL);
+	if (!digest)
+		goto cleanup_tfm;
+
+	digest->tfm = tfm;
+	digest->flags = 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;
+
+	digest_size = crypto_shash_digestsize(tfm);
+
+	raw_fingerprint = kmalloc(digest_size, GFP_KERNEL);
+	if (!raw_fingerprint)
+		goto cleanup_hash;
+
+	ret = crypto_shash_final(digest, raw_fingerprint);
+	if (ret < 0)
+		goto cleanup_raw_fingerprint;
+
+	fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
+	if (!fingerprint)
+		goto cleanup_raw_fingerprint;
+
+	offset = digest_size - 8;
+	pr_debug("offset %u/%u\n", offset, digest_size);
+
+	for (i = 0; i < digest_size; i++)
+		sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+	pr_debug("fingerprint %s\n", fingerprint);
+
+	ctx->raw_fingerprint = raw_fingerprint;
+	ctx->fingerprint = fingerprint;
+	ret = 0;
+cleanup_raw_fingerprint:
+	if (ret < 0)
+		kfree(raw_fingerprint);
+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;
+	int ret;
+
+	kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+	if (ctx->fingerprint) {
+		kleave(" = -ENOKEY [already]");
+		return -EBADMSG;
+	}
+
+	pub = kzalloc(sizeof(struct public_key), 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;
+
+	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");
+	kfree(pub->key);
+	kfree(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;
+	int fingerprint_len = strlen(ctx->fingerprint) / 2;
+
+	kids = kzalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
+	if (!kids)
+		return kids;
+
+	kid = asymmetric_key_generate_id(ctx->raw_fingerprint, fingerprint_len,
+					 NULL, 0);
+	if (IS_ERR(kid))
+		goto error;
+
+	kids->id[0] = kid;
+	kids->id[1] = kmemdup(kid, sizeof(kid) + 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;
+
+	/* 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);
+	kfree(ctx.fingerprint);
+	kfree(ctx.raw_fingerprint);
+	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.17.1


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

* [RFC][PATCH 07/12] KEYS: Provide PGP key description autogeneration
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (5 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 06/12] KEYS: PGP data parser Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification Roberto Sassu
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

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 []

Changelog

v0:
- fix style issues (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/pgp_public_key.c | 46 ++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/crypto/asymmetric_keys/pgp_public_key.c b/crypto/asymmetric_keys/pgp_public_key.c
index a5ce146a1250..371a3e721f69 100644
--- a/crypto/asymmetric_keys/pgp_public_key.c
+++ b/crypto/asymmetric_keys/pgp_public_key.c
@@ -35,6 +35,9 @@ struct pgp_key_data_parse_context {
 	struct public_key *pub;
 	unsigned char *raw_fingerprint;
 	char *fingerprint;
+	const char *user_id;
+	size_t user_id_len;
+	size_t fingerprint_len;
 };
 
 /*
@@ -155,6 +158,7 @@ static int pgp_generate_fingerprint(struct pgp_key_data_parse_context *ctx,
 	if (ret < 0)
 		goto cleanup_raw_fingerprint;
 
+	ctx->fingerprint_len = digest_size * 2;
 	fingerprint = kmalloc(digest_size * 2 + 1, GFP_KERNEL);
 	if (!fingerprint)
 		goto cleanup_raw_fingerprint;
@@ -199,6 +203,13 @@ static int pgp_process_public_key(struct pgp_parse_context *context,
 
 	kenter(",%u,%u,,%zu", type, headerlen, datalen);
 
+	if (type == PGP_PKT_USER_ID) {
+		ctx->user_id = data;
+		ctx->user_id_len = datalen;
+		kleave(" = 0 [user ID]");
+		return 0;
+	}
+
 	if (ctx->fingerprint) {
 		kleave(" = -ENOKEY [already]");
 		return -EBADMSG;
@@ -291,13 +302,46 @@ 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);
 	if (ret < 0)
 		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, flen = ctx.fingerprint_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] = ' ';
+		memcpy(prep->description + ulen + 1,
+		       ctx.fingerprint + flen - 8, 8);
+		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.17.1


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

* [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (6 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 07/12] KEYS: Provide PGP key description autogeneration Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 09/12] verification: introduce verify_pgp_signature() Roberto Sassu
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

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 the
next patch provides.

Changelog

v0:
- replace algorithm identifiers with strings (Roberto Sassu)
- don't check key capabilities (Roberto Sassu)
- replace "public_key:%08x%08x" with "id:%08x%08x" (Roberto Sassu)
- switch from session to user keyring (Roberto Sassu)
- search user keyring only if no keyring was provided, so that the
  trustworthiness of the signature depends on the type of keyring
  containing the key used for signature verification (Roberto Sassu)
- don't parse MPIs (Roberto Sassu)
- introduce pgp_verify_sig() (Roberto Sassu)
- fix digest calculation for V3 signature packets (Roberto Sassu)
- fix style issues (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Makefile        |   3 +-
 crypto/asymmetric_keys/pgp_signature.c | 428 +++++++++++++++++++++++++
 include/linux/pgp_sig.h                |  21 ++
 3 files changed, 451 insertions(+), 1 deletion(-)
 create mode 100644 crypto/asymmetric_keys/pgp_signature.c
 create mode 100644 include/linux/pgp_sig.h

diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index a68f9a5d1746..e2aeb2a4b6a6 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -94,4 +94,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..771cdabf5cf9
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_signature.c
@@ -0,0 +1,428 @@
+/* PGP public key signature verification [RFC 4880]
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#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/pgp_sig.h>
+#include <linux/pgplib.h>
+#include <linux/err.h>
+#include <keys/asymmetric-type.h>
+#include <crypto/public_key.h>
+#include <crypto/hash.h>
+#include <crypto/hash_info.h>
+#include "pgp_parser.h"
+
+static const struct {
+	enum hash_algo algo : 8;
+} pgp_pubkey_hash[PGP_HASH__LAST] = {
+	[PGP_HASH_MD5].algo		= HASH_ALGO_MD5,
+	[PGP_HASH_SHA1].algo		= HASH_ALGO_SHA1,
+	[PGP_HASH_RIPE_MD_160].algo	= HASH_ALGO_RIPE_MD_160,
+	[PGP_HASH_SHA256].algo		= HASH_ALGO_SHA256,
+	[PGP_HASH_SHA384].algo		= HASH_ALGO_SHA384,
+	[PGP_HASH_SHA512].algo		= HASH_ALGO_SHA512,
+	[PGP_HASH_SHA224].algo		= HASH_ALGO_SHA224,
+};
+
+struct pgp_sig_verify {
+	struct public_key_signature sig;
+	const struct public_key *pub;
+	struct key *key;
+	u8 signed_hash_msw[2];
+	struct shash_desc hash;
+};
+
+/*
+ * Find a key in the given keyring by issuer and authority.
+ */
+static struct key *pgp_request_asymmetric_key(struct key *keyring,
+					      struct pgp_sig_parameters *params)
+{
+	key_ref_t key;
+	char *id;
+
+	if (params->pubkey_algo >= PGP_PUBKEY__LAST) {
+		WARN(1, "Unknown public key algorithm %d\n",
+		     params->pubkey_algo);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Construct an identifier. */
+	id = kasprintf(GFP_KERNEL,
+		       "id:%08x%08x",
+		       be32_to_cpu(params->issuer32[0]),
+		       be32_to_cpu(params->issuer32[1]));
+	if (!id)
+		return ERR_PTR(-ENOMEM);
+
+	pr_debug("Look up key: \"%s\"\n", id);
+
+	if (!keyring)
+		keyring = current_cred()->user->uid_keyring;
+
+	if (!keyring)
+		return ERR_PTR(-ENOKEY);
+
+	key = keyring_search(make_key_ref(keyring, 1),
+			     &key_type_asymmetric, id);
+
+	if (IS_ERR(key))
+		pr_debug("Request for public key '%s' err %ld\n",
+			 id, PTR_ERR(key));
+
+	kfree(id);
+
+	if (IS_ERR(key)) {
+		switch (PTR_ERR(key)) {
+			/* Hide some search errors */
+		case -EACCES:
+		case -ENOTDIR:
+		case -EAGAIN:
+			return ERR_PTR(-ENOKEY);
+		default:
+			return ERR_CAST(key);
+		}
+	}
+
+	kleave(" = 0 [%x]", key_serial(key_ref_to_ptr(key)));
+	return key_ref_to_ptr(key);
+}
+
+struct pgp_sig_parse_context {
+	struct pgp_parse_context pgp;
+	struct pgp_sig_parameters params;
+};
+
+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);
+
+	return pgp_parse_sig_params(&data, &datalen, &ctx->params);
+}
+
+/**
+ * pgp_verify_sig_begin - Begin the process of verifying a signature
+ * @keyring: Ring of keys to search for the public key
+ * @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 check that it matches one
+ * of the keys in the supplied keyring.
+ */
+static struct pgp_sig_verify *pgp_verify_sig_begin(struct key *keyring,
+						   const u8 *sigdata,
+						   size_t siglen)
+{
+	struct pgp_sig_parse_context p;
+	const struct public_key *pub;
+	struct pgp_sig_verify *ctx;
+	struct crypto_shash *tfm;
+	struct key *key;
+	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;
+	ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	/* 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);
+		return ERR_PTR(-ENOPKG);
+	}
+
+	pr_debug("Signature generated with %s hash\n",
+		 pgp_hash_algorithms[p.params.hash_algo]);
+
+	if (p.params.signature_type != PGP_SIG_BINARY_DOCUMENT_SIG &&
+	    p.params.signature_type != PGP_SIG_STANDALONE_SIG) {
+		/* We don't want to canonicalise */
+		kleave(" = -EOPNOTSUPP [canon]");
+		return ERR_PTR(-EOPNOTSUPP);
+	}
+
+	/* Now we need to find a key to use */
+	key = pgp_request_asymmetric_key(keyring, &p.params);
+	if (IS_ERR(key)) {
+		kleave(" = %ld [reqkey]", PTR_ERR(key));
+		return ERR_CAST(key);
+	}
+	pub = key->payload.data[asym_crypto];
+
+	if (strcmp(pkey_algo, pub->pkey_algo)) {
+		kleave(" = -EKEYREJECTED [wrong pk algo]");
+		ret = -EKEYREJECTED;
+		goto error_have_key;
+	}
+
+	/* 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));
+		goto error_have_key;
+	}
+
+	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) + desc_size + digest_size, GFP_KERNEL);
+	if (!ctx) {
+		ret = -ENOMEM;
+		goto error_have_shash;
+	}
+
+	ctx->key		= key;
+	ctx->pub		= pub;
+	ctx->sig.encoding	= "pkcs1";
+	ctx->sig.pkey_algo	= pub->pkey_algo;
+	ctx->sig.hash_algo	= pgp_hash_algorithms[p.params.hash_algo];
+	ctx->sig.digest		= (u8 *)ctx + sizeof(*ctx) + desc_size;
+	ctx->sig.digest_size	= digest_size;
+	ctx->hash.tfm		= tfm;
+	ctx->hash.flags		= CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	ret = crypto_shash_init(&ctx->hash);
+	if (ret < 0)
+		goto error_have_shash;
+
+	kleave(" = %p", ctx);
+	return ctx;
+
+error_have_shash:
+	crypto_free_shash(tfm);
+error_have_key:
+	key_put(key);
+	return ERR_PTR(ret);
+
+unsupported_pkey_algo:
+	pr_debug("Unsupported public key algorithm %u\n",
+		 p.params.pubkey_algo);
+	return ERR_PTR(-ENOPKG);
+}
+
+/*
+ * Load data into the hash
+ */
+static int pgp_verify_sig_add_data(struct pgp_sig_verify *ctx,
+				   const void *data, size_t datalen)
+{
+	return crypto_shash_update(&ctx->hash, data, datalen);
+}
+
+struct pgp_sig_digest_context {
+	struct pgp_parse_context pgp;
+	struct pgp_sig_verify *ctx;
+};
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * to the hash.
+ */
+static int pgp_digest_signature(struct pgp_parse_context *context,
+				enum pgp_packet_tag type,
+				u8 headerlen,
+				const u8 *data,
+				size_t datalen)
+{
+	struct pgp_sig_digest_context *pgp_ctx =
+		container_of(context, struct pgp_sig_digest_context, pgp);
+	struct pgp_sig_verify *ctx = pgp_ctx->ctx;
+	struct public_key_signature *sig = &ctx->sig;
+	enum pgp_signature_version version;
+	unsigned int nbytes, nbytes_alloc;
+	int ret;
+
+	kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+	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)
+		return ret;
+
+	if (datalen != nbytes + 2) {
+		kleave(" = -EBADMSG [trailer %zu]", datalen);
+		return -EBADMSG;
+	}
+
+	nbytes_alloc = DIV_ROUND_UP(nbytes, 8) * 8;
+
+	sig->s = kzalloc(nbytes_alloc, GFP_KERNEL);
+	if (!sig->s)
+		return -ENOMEM;
+
+	memcpy(sig->s + nbytes_alloc - nbytes, data + 2, nbytes);
+	sig->s_size = nbytes_alloc;
+
+	kleave(" = 0");
+	return 0;
+}
+
+/*
+ * The data is now all loaded into the hash; load the metadata, finalise the
+ * hash and perform the verification step.
+ */
+static int pgp_verify_sig_end(struct pgp_sig_verify *ctx,
+			      const u8 *digest, size_t digest_size,
+			      const u8 *sigdata, size_t siglen)
+{
+	struct pgp_sig_digest_context p;
+	int ret;
+
+	kenter("");
+
+	/* Firstly we add metadata, starting with some of the data from the
+	 * signature packet
+	 */
+	p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+	p.pgp.process_packet = pgp_digest_signature;
+	p.ctx = ctx;
+	ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_final(&ctx->hash, ctx->sig.digest);
+	if (ret < 0)
+		goto error;
+
+	if (digest && digest_size == crypto_shash_digestsize(ctx->hash.tfm))
+		memcpy(ctx->sig.digest, digest, digest_size);
+
+	pr_debug("hash: %*phN\n", ctx->sig.digest_size, ctx->sig.digest);
+
+	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;
+		return ret;
+	}
+
+	ret = verify_signature(ctx->key, &ctx->sig);
+
+error:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Cancel an in-progress data loading
+ */
+static void pgp_verify_sig_cancel(struct pgp_sig_verify *ctx)
+{
+	kenter("");
+
+	/* !!! Do we need to tell the crypto layer to cancel too? */
+	key_put(ctx->key);
+	crypto_free_shash(ctx->hash.tfm);
+	kfree(ctx->sig.s);
+	kfree(ctx);
+
+	kleave("");
+}
+
+int pgp_verify_sig(struct key *keyring, const u8 *raw_data, size_t raw_datalen,
+		   const u8 *digest, size_t digest_size, const u8 *sig_data,
+		   size_t sig_datalen)
+{
+	struct pgp_sig_verify *sig;
+	int ret;
+
+	sig = pgp_verify_sig_begin(keyring, sig_data, sig_datalen);
+	if (IS_ERR(sig))
+		return PTR_ERR(sig);
+
+	ret = pgp_verify_sig_add_data(sig, raw_data, raw_datalen);
+	if (ret < 0)
+		goto error_cancel;
+
+	ret = pgp_verify_sig_end(sig, digest, digest_size,
+				 sig_data, sig_datalen);
+error_cancel:
+	pgp_verify_sig_cancel(sig);
+	return ret;
+}
diff --git a/include/linux/pgp_sig.h b/include/linux/pgp_sig.h
new file mode 100644
index 000000000000..d7c6fb6c61aa
--- /dev/null
+++ b/include/linux/pgp_sig.h
@@ -0,0 +1,21 @@
+/* PGP signature processing
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_PGP_SIG_H
+#define _LINUX_PGP_SIG_H
+
+struct key;
+
+int pgp_verify_sig(struct key *keyring, const u8 *raw_data, size_t raw_datalen,
+		   const u8 *digest, size_t digest_size, const u8 *sig_data,
+		   size_t sig_datalen);
+
+#endif /* _LINUX_PGP_SIG_H */
-- 
2.17.1


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

* [RFC][PATCH 09/12] verification: introduce verify_pgp_signature()
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (7 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 10/12] PGP: Provide a key type for testing PGP signatures Roberto Sassu
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel,
	silviu.vlasceanu, Roberto Sassu

Introduce verify_pgp_signature() to to verify PGP signatures from data or
a digest. One user of this function would be IMA, that can verify the
signature of RPM headers when appraisal is enabled.

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

diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index 81728717523d..e4c59a5c7a9d 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -19,6 +19,7 @@
 #include <keys/asymmetric-type.h>
 #include <keys/system_keyring.h>
 #include <crypto/pkcs7.h>
+#include <linux/pgp_sig.h>
 
 static struct key *builtin_trusted_keys;
 #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
@@ -265,4 +266,42 @@ int verify_pkcs7_signature(const void *data, size_t len,
 }
 EXPORT_SYMBOL_GPL(verify_pkcs7_signature);
 
+/**
+ * verify_pgp_signature - Verify a PGP-based signature on system data.
+ * @data: The data to be verified (NULL if expecting internal data).
+ * @len: Size of @data.
+ * @digest: Digest for signature verification.
+ * @digest_size: Size of @digest.
+ * @raw_pgp: The PGP message that is the signature.
+ * @pgp_len: Size of @raw_pgp.
+ * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only,
+ *				       (void *)1UL for all trusted keys).
+ */
+int verify_pgp_signature(const void *data, size_t len,
+			 const void *digest, size_t digest_size,
+			 const void *raw_pgp, size_t pgp_len,
+			 struct key *trusted_keys)
+{
+	int ret = -ENOTSUPP;
+
+#ifdef CONFIG_PGP_KEY_PARSER
+	if (!trusted_keys) {
+		trusted_keys = builtin_trusted_keys;
+	} else if (trusted_keys == (void *)1UL) {
+#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
+		trusted_keys = secondary_trusted_keys;
+#else
+		trusted_keys = builtin_trusted_keys;
+#endif
+	}
+
+	ret = pgp_verify_sig(trusted_keys, data, len, digest, digest_size,
+			     raw_pgp, pgp_len);
+
+#endif /* CONFIG_PGP_KEY_PARSER */
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(verify_pgp_signature);
+
 #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
diff --git a/include/linux/verification.h b/include/linux/verification.h
index cfa4730d607a..6fd7bf60efaa 100644
--- a/include/linux/verification.h
+++ b/include/linux/verification.h
@@ -45,6 +45,11 @@ extern int verify_pkcs7_signature(const void *data, size_t len,
 						      size_t asn1hdrlen),
 				  void *ctx);
 
+extern int verify_pgp_signature(const void *data, size_t len,
+				const void *digest, size_t digest_size,
+				const void *raw_pgp, size_t pgp_len,
+				struct key *trusted_keys);
+
 #ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
 extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
 				   struct key *trusted_keys,
-- 
2.17.1


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

* [RFC][PATCH 10/12] PGP: Provide a key type for testing PGP signatures
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (8 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 09/12] verification: introduce verify_pgp_signature() Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 11/12] KEYS: Provide a function to load keys from a PGP keyring blob Roberto Sassu
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

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

Changelog

v0:
- use verify_pgp_signature() to verify signatures with builtin_trusted_keys
  or secondary_trusted_keys (Roberto Sassu)
- use pgp_verify_sig() to verify signatures with keys in the user keyring
  (Roberto Sassu)
- replace user_instantiate with generic_key_instantiate (Roberto Sassu)
- remove .def_lookup_type and .match methods (Roberto Sassu)
- fix style issues (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig        |  13 +++
 crypto/asymmetric_keys/Makefile       |   4 +
 crypto/asymmetric_keys/pgp_library.c  |  64 +++++++++++++
 crypto/asymmetric_keys/pgp_test_key.c | 132 ++++++++++++++++++++++++++
 include/linux/pgplib.h                |  15 +++
 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 356b85fc34bd..8226d06fe1e0 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -110,4 +110,17 @@ config PGP_KEY_PARSER
 	  for key data and provides the ability to instantiate a crypto key
 	  from a public key packet found inside the blob.
 
+config PGP_TEST_KEY
+	tristate "PGP testing key type"
+	depends on PGP_KEY_PARSER
+	depends on SYSTEM_DATA_VERIFICATION
+	help
+	  This option provides a type of key that can be loaded up from a
+	  PGP message - provided the message is signed by a trusted key.  If
+	  it is, the PGP wrapper is discarded and reading the key returns
+	  just the payload.  If it isn't, adding the key will fail with an
+	  error.
+
+	  This is intended for testing the PGP parser.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index e2aeb2a4b6a6..48bbe2b1d446 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -96,3 +96,7 @@ 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.o
+pgp_test-y := \
+	pgp_test_key.o \
+	pgp_signature.o
diff --git a/crypto/asymmetric_keys/pgp_library.c b/crypto/asymmetric_keys/pgp_library.c
index 13bfc9db1ae4..3a2bd73f1915 100644
--- a/crypto/asymmetric_keys/pgp_library.c
+++ b/crypto/asymmetric_keys/pgp_library.c
@@ -559,3 +559,67 @@ int pgp_parse_sig_params(const u8 **_data, size_t *_datalen,
 	return 0;
 }
 EXPORT_SYMBOL_GPL(pgp_parse_sig_params);
+
+#if IS_ENABLED(CONFIG_PGP_TEST_KEY)
+
+/**
+ * pgp_parse_literal_data - Parse basic params from a PGP literal data packet
+ * @data: Content of packet
+ * @datalen: Length of packet remaining
+ * @p: The basic parameters
+ *
+ * Parse the basic parameters from a PGP literal data packet [RFC 4880: 5.9]
+ * that are needed to work out what form the data is in and where it is.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
+int pgp_parse_literal_data(const u8 *data, size_t datalen,
+			   struct pgp_literal_data_parameters *p)
+{
+	unsigned int tmp;
+
+	pr_devel("-->%s(,%zu,,)\n", __func__, datalen);
+
+	if (datalen < 6)
+		goto too_short;
+	datalen -= 6;
+
+	p->format = *data++;
+	switch (p->format) {
+	case PGP_LIT_FORMAT_BINARY:
+	case PGP_LIT_FORMAT_TEXT:
+	case PGP_LIT_FORMAT_TEXT_UTF8:
+		break;
+	default:
+		pr_debug("Literal data packet with unhandled format %02x\n",
+			 p->format);
+		return -EBADMSG;
+	}
+
+	p->filename_len = *data++;
+	p->filename_offset = 2;
+	if (datalen < p->filename_len)
+		goto too_short;
+	data += p->filename_len;
+	datalen -= p->filename_len;
+
+	tmp  = *data++ << 24;
+	tmp |= *data++ << 16;
+	tmp |= *data++ << 8;
+	tmp |= *data++;
+	p->time = tmp;
+
+	p->content_offset = 6 + p->filename_len;
+	p->content_len = datalen;
+
+	pr_devel("%x,%u,%x,%u\n",
+		 p->format, p->filename_len, p->time, p->content_len);
+	return 0;
+
+too_short:
+	pr_debug("Literal data packet too short\n");
+	return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(pgp_parse_literal_data);
+
+#endif /* CONFIG_PGP_TEST_KEY */
diff --git a/crypto/asymmetric_keys/pgp_test_key.c b/crypto/asymmetric_keys/pgp_test_key.c
new file mode 100644
index 000000000000..55bc6cad8559
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_test_key.c
@@ -0,0 +1,132 @@
+/* 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)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#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/pgp.h>
+#include <linux/pgplib.h>
+#include <linux/pgp_sig.h>
+#include <linux/module.h>
+#include <linux/verification.h>
+#include <keys/user-type.h>
+#include <keys/system_keyring.h>
+#include "pgp_parser.h"
+
+MODULE_LICENSE("GPL");
+
+struct pgp_test_parse_context {
+	struct pgp_parse_context pgp;
+	struct pgp_literal_data_parameters params;
+	const void *content;
+};
+
+static int 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;
+	int ret;
+
+	kenter("");
+
+	if (!current_cred()->user->uid_keyring)
+		return -ENOKEY;
+
+	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,
+				   NULL, 0, prep->data, prep->datalen, NULL);
+	if (ret < 0) {
+		ret = pgp_verify_sig(NULL, p.content, p.params.content_len,
+				     NULL, 0, prep->data, prep->datalen);
+		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/include/linux/pgplib.h b/include/linux/pgplib.h
index 44c8a07b32e3..21999bfe8e2a 100644
--- a/include/linux/pgplib.h
+++ b/include/linux/pgplib.h
@@ -66,6 +66,21 @@ 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 */
 
 #endif /* CONFIG_PGP_LIBRARY */
 
-- 
2.17.1


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

* [RFC][PATCH 11/12] KEYS: Provide a function to load keys from a PGP keyring blob
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (9 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 10/12] PGP: Provide a key type for testing PGP signatures Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 10:24 ` [RFC][PATCH 12/12] KEYS: Introduce load_pgp_public_keyring() Roberto Sassu
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel, silviu.vlasceanu

From: David Howells <dhowells@redhat.com>

Provide a function to load keys from a PGP keyring blob for use in
initialising the module signing key keyring:

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

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

The keys are attached to the keyring supplied.

Looking as root in /proc/keys after the module signing 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.

Changelog

v0:
- avoid Kconfig circular dependency (Roberto Sassu)
- modify flags passed to key_create_or_update() (Roberto Sassu)
- don't process Public-Subkey packets (Roberto Sassu)

Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 crypto/asymmetric_keys/Kconfig       |   8 ++
 crypto/asymmetric_keys/Makefile      |   1 +
 crypto/asymmetric_keys/pgp_preload.c | 118 +++++++++++++++++++++++++++
 include/linux/pgp_sig.h              |   3 +
 4 files changed, 130 insertions(+)
 create mode 100644 crypto/asymmetric_keys/pgp_preload.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 8226d06fe1e0..f0b4e3ccf1b8 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -123,4 +123,12 @@ config PGP_TEST_KEY
 
 	  This is intended for testing the PGP parser.
 
+config PGP_PRELOAD
+	bool "PGP public key preloading facility"
+	select PGP_KEY_PARSER
+	help
+	  This option provides a facility for the kernel to preload PGP-wrapped
+	  bundles of keys during boot.  It is used by module signing to load
+	  the module signing keys for example.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 48bbe2b1d446..c04a103a99c5 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -91,6 +91,7 @@ $(obj)/tpm.asn1.o: $(obj)/tpm.asn1.c $(obj)/tpm.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..7cabade16780
--- /dev/null
+++ b/crypto/asymmetric_keys/pgp_preload.c
@@ -0,0 +1,118 @@
+/* Cryptographic key request handling
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ *
+ * See Documentation/security/keys-crypto.txt
+ */
+
+#include <linux/module.h>
+#include <linux/key.h>
+#include <linux/pgp_sig.h>
+#include <linux/pgplib.h>
+#include <linux/err.h>
+#include <keys/asymmetric-type.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 int __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))
+		return PTR_ERR(key);
+
+	pr_notice("Loaded PGP key '%s'\n",
+		  key_ref_to_ptr(key)->description);
+
+	key_ref_put(key);
+	return 0;
+}
+
+/*
+ * 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);
+	int ret;
+
+	if (ctx->found_key) {
+		ctx->key_end = data - headerlen;
+		ret = create_pgp_key(ctx);
+		if (ret < 0)
+			return ret;
+	}
+
+	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;
+		return create_pgp_key(&ctx);
+	}
+	return 0;
+}
diff --git a/include/linux/pgp_sig.h b/include/linux/pgp_sig.h
index d7c6fb6c61aa..c0114cfc2f20 100644
--- a/include/linux/pgp_sig.h
+++ b/include/linux/pgp_sig.h
@@ -18,4 +18,7 @@ int pgp_verify_sig(struct key *keyring, const u8 *raw_data, size_t raw_datalen,
 		   const u8 *digest, size_t digest_size, const u8 *sig_data,
 		   size_t sig_datalen);
 
+int __init preload_pgp_keys(const u8 *pgpdata, size_t pgpdatalen,
+			    struct key *keyring);
+
 #endif /* _LINUX_PGP_SIG_H */
-- 
2.17.1


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

* [RFC][PATCH 12/12] KEYS: Introduce load_pgp_public_keyring()
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (10 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 11/12] KEYS: Provide a function to load keys from a PGP keyring blob Roberto Sassu
@ 2018-11-12 10:24 ` Roberto Sassu
  2018-11-12 12:31 ` [RFC][PATCH 04/12] PGPLIB: Basic packet parser David Howells
                   ` (2 subsequent siblings)
  14 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 10:24 UTC (permalink / raw)
  To: dhowells, dwmw2, herbert, davem
  Cc: keyrings, linux-crypto, linux-integrity, linux-kernel,
	silviu.vlasceanu, Roberto Sassu

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

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 certs/Kconfig          |  7 +++++++
 certs/Makefile         |  3 +++
 certs/system_keyring.c | 25 +++++++++++++++++++++++++
 3 files changed, 35 insertions(+)

diff --git a/certs/Kconfig b/certs/Kconfig
index c94e93d8bccf..71be583e55a4 100644
--- a/certs/Kconfig
+++ b/certs/Kconfig
@@ -83,4 +83,11 @@ config SYSTEM_BLACKLIST_HASH_LIST
 	  wrapper to incorporate the list into the kernel.  Each <hash> should
 	  be a string of hex digits.
 
+config PGP_PRELOAD_PUBLIC_KEYS
+	bool "Preload PGP public keys"
+	select PGP_PRELOAD
+	default n
+	help
+	  Provide a keyring of PGP public keys.
+
 endmenu
diff --git a/certs/Makefile b/certs/Makefile
index 5d0999b9e21b..4338af9182e3 100644
--- a/certs/Makefile
+++ b/certs/Makefile
@@ -4,6 +4,9 @@
 #
 
 obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
+ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS
+certs/system_keyring.o: pubring.gpg
+endif
 obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o
 ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),"")
 obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
diff --git a/certs/system_keyring.c b/certs/system_keyring.c
index e4c59a5c7a9d..5dacb124133f 100644
--- a/certs/system_keyring.c
+++ b/certs/system_keyring.c
@@ -189,6 +189,31 @@ static __init int load_system_certificate_list(void)
 }
 late_initcall(load_system_certificate_list);
 
+#ifdef CONFIG_PGP_PRELOAD_PUBLIC_KEYS
+extern __initconst const u8 pgp_public_keys[];
+extern __initconst const u8 pgp_public_keys_end[];
+asm(".section .init.data,\"aw\"\n"
+	"pgp_public_keys:\n"
+	".incbin \"pubring.gpg\"\n"
+	"pgp_public_keys_end:");
+
+/*
+ * Load a list of PGP keys.
+ */
+static __init int load_pgp_public_keyring(void)
+{
+	pr_notice("Load PGP public keys\n");
+
+	if (preload_pgp_keys(pgp_public_keys,
+			     pgp_public_keys_end - pgp_public_keys,
+			     builtin_trusted_keys) < 0)
+		pr_err("Can't load PGP public keys\n");
+
+	return 0;
+}
+late_initcall(load_pgp_public_keyring);
+#endif /* CONFIG_PGP_PRELOAD_PUBLIC_KEYS */
+
 #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
 
 /**
-- 
2.17.1


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

* Re: [RFC][PATCH 04/12] PGPLIB: Basic packet parser
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (11 preceding siblings ...)
  2018-11-12 10:24 ` [RFC][PATCH 12/12] KEYS: Introduce load_pgp_public_keyring() Roberto Sassu
@ 2018-11-12 12:31 ` David Howells
  2018-11-12 12:35 ` [RFC][PATCH 05/12] PGPLIB: Signature parser David Howells
  2018-11-12 12:43 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification David Howells
  14 siblings, 0 replies; 19+ messages in thread
From: David Howells @ 2018-11-12 12:31 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: dhowells, dwmw2, herbert, davem, keyrings, linux-crypto,
	linux-integrity, linux-kernel, silviu.vlasceanu

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

> +	size_t size, datalen = *_datalen;
> ...
> +	if ((int)size < 0)
> +		goto too_big;

Hmmm...  "if (size > INT_MAX)" is probably better.

David

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

* Re: [RFC][PATCH 05/12] PGPLIB: Signature parser
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (12 preceding siblings ...)
  2018-11-12 12:31 ` [RFC][PATCH 04/12] PGPLIB: Basic packet parser David Howells
@ 2018-11-12 12:35 ` David Howells
  2018-11-12 12:43 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification David Howells
  14 siblings, 0 replies; 19+ messages in thread
From: David Howells @ 2018-11-12 12:35 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: dhowells, dwmw2, herbert, davem, keyrings, linux-crypto,
	linux-integrity, linux-kernel, silviu.vlasceanu

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

> -	pr_devel("datalen=%zu size=%zu", datalen, size);
> +	pr_devel("datalen=%zu size=%zu\n", datalen, size);

This needs rolling back into a previous patch.

> +	if ((int)size < 0)

size > INT_MAX

David

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

* Re: [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification
  2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
                   ` (13 preceding siblings ...)
  2018-11-12 12:35 ` [RFC][PATCH 05/12] PGPLIB: Signature parser David Howells
@ 2018-11-12 12:43 ` David Howells
  2018-11-12 14:22   ` Roberto Sassu
  2018-12-10 16:58   ` David Howells
  14 siblings, 2 replies; 19+ messages in thread
From: David Howells @ 2018-11-12 12:43 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: dhowells, dwmw2, herbert, davem, keyrings, linux-crypto,
	linux-integrity, linux-kernel, silviu.vlasceanu

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

> - switch from session to user keyring (Roberto Sassu)
> - search user keyring only if no keyring was provided, so that the
>   trustworthiness of the signature depends on the type of keyring
>   containing the key used for signature verification (Roberto Sassu)

Er.  No.  You should search the session keyring.  This may contain a link to
the user keyring (pam_keyinit emplaces one).

You need to consider what it is that the patch trying to achieve.

David

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

* Re: [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification
  2018-11-12 12:43 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification David Howells
@ 2018-11-12 14:22   ` Roberto Sassu
  2018-12-10 16:58   ` David Howells
  1 sibling, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-11-12 14:22 UTC (permalink / raw)
  To: David Howells
  Cc: dwmw2, herbert, davem, keyrings, linux-crypto, linux-integrity,
	linux-kernel, silviu.vlasceanu

On 11/12/2018 1:43 PM, David Howells wrote:
> Roberto Sassu <roberto.sassu@huawei.com> wrote:
> 
>> - switch from session to user keyring (Roberto Sassu)
>> - search user keyring only if no keyring was provided, so that the
>>    trustworthiness of the signature depends on the type of keyring
>>    containing the key used for signature verification (Roberto Sassu)
> 
> Er.  No.  You should search the session keyring.  This may contain a link to
> the user keyring (pam_keyinit emplaces one).

Ok. Unfortunately, I was encountering some issues:
---
[   20.477851] BUG: sleeping function called from invalid context at 
mm/slab.h:421
[   20.486987] in_atomic(): 0, irqs_disabled(): 0, pid: 739, name: keyctl
[   20.497393] 4 locks held by keyctl/739:
[   20.500056]  #0: 00000000bd9d7a18 (key_types_sem){....}, at: 
key_type_lookup+0x16/0x80
[   20.503065]  #1: 000000009f5fc7ec (&type->lock_class){....}, at: 
__key_link_begin+0x3f/0x100
[   20.506062]  #2: 00000000cc8bdc61 (key_construction_mutex){....}, at: 
__key_instantiate_and_link+0x30/0x150
[   20.509335]  #3: 000000001dff342f (rcu_read_lock){....}, at: 
pgp_verify_sig+0x57e/0x6a0
[   20.511998] Preemption disabled at:
[   20.512015] [<ffffffff818bc86f>] __mutex_lock+0x5f/0x940
[   20.514885] CPU: 7 PID: 739 Comm: keyctl Not tainted 4.20.0-rc2+ #1138
[   20.516911] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), 
BIOS 1.10.2-1ubuntu1 04/01/2014
[   20.519577] Call Trace:
[   20.520384]  dump_stack+0x5c/0x7b
[   20.521423]  ? __mutex_lock+0x5f/0x940
[   20.523296]  ___might_sleep+0x12f/0x180
[   20.524458]  __kmalloc+0x24c/0x300
[   20.525505]  ? asymmetric_key_hex_to_key_id.part.8+0x30/0x80
[   20.527181]  ? keyring_search_aux+0xbb/0xf0
[   20.528430]  asymmetric_key_hex_to_key_id.part.8+0x30/0x80
[   20.530025]  ? asymmetric_key_id_partial+0x40/0x40
[   20.531422]  asymmetric_key_match_preparse+0x6b/0x90
[   20.532868]  keyring_search+0x79/0xd0
[   20.533938]  ? keyring_alloc+0x80/0x80
[   20.535068]  pgp_verify_sig+0x5d1/0x6a0
[   20.536212]  ? pgp_verify_sig+0x57e/0x6a0
[   20.537389]  ? pgp_key_parse+0x2a0/0x2a0
[   20.538565]  ? __mutex_lock+0x89/0x940
[   20.539701]  ? pgp_test_instantiate+0xb9/0x150 [pgp_test]
[   20.541276]  pgp_test_instantiate+0xb9/0x150 [pgp_test]
---

> You need to consider what it is that the patch trying to achieve.

I understood that the purpose is to check PGP signatures with built-in
keys or keys provided by the user. Since using the session keyring
caused the issue I reported, I thought it was ok to use the user
keyring.

Just a note: the original patches were relying on KEY_FLAG_TRUSTED to
determine if a key is trusted; now the trustworthiness depends on the
type of keyring passed to pgp_verify_sig(). I removed the additional key
search in the user (session) keyring to prevent that signature
verification is done with a key provided by the user even when the
caller of pgp_verify_sig() expects that a trusted key is used. The
search in the session keyring is done if the caller of pgp_verify_sig()
sets the keyring pointer to NULL.

Roberto


> David
> 

-- 
HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Bo PENG, Jian LI, Yanli SHI

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

* Re: [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification
  2018-11-12 12:43 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification David Howells
  2018-11-12 14:22   ` Roberto Sassu
@ 2018-12-10 16:58   ` David Howells
  2018-12-10 18:04     ` Roberto Sassu
  1 sibling, 1 reply; 19+ messages in thread
From: David Howells @ 2018-12-10 16:58 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: dhowells, dwmw2, herbert, davem, keyrings, linux-crypto,
	linux-integrity, linux-kernel, silviu.vlasceanu

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

> > You need to consider what it is that the patch trying to achieve.
> 
> I understood that the purpose is to check PGP signatures with built-in
> keys or keys provided by the user. Since using the session keyring
> caused the issue I reported, I thought it was ok to use the user
> keyring.
> 
> Just a note: the original patches were relying on KEY_FLAG_TRUSTED to
> determine if a key is trusted; now the trustworthiness depends on the
> type of keyring passed to pgp_verify_sig(). I removed the additional key
> search in the user (session) keyring to prevent that signature
> verification is done with a key provided by the user even when the
> caller of pgp_verify_sig() expects that a trusted key is used. The
> search in the session keyring is done if the caller of pgp_verify_sig()
> sets the keyring pointer to NULL.

Thinking about these patches further, as you point out, the way that trust is
computed has changed.  There is no no KEY_FLAG_TRUSTED; rather, a key is
assumed to be trusted if it's on a trusted keyring.

*Getting* it onto that trusted keyring is then the trick.  There are two ways:

 (1) A key can be loaded from a trusted source during boot (say a compiled in
     set of keys).

 (2) A key can be added to that keyring later, provided that the key is
     verified by a key already in the ring and the ring hasn't been closed.

What do we need to check PGP signatures?  Blobs or keys as well?

Why does the user keyring need to be a fallback?  (I know the session keyring
used to be a fallback when I first did these, but things have changed since
then).

Should we have a separate built-in keyring for PGP keys?  Actually, I suspect
we should probably mark keys in some way with what they're permitted to be
used for.

David

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

* Re: [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification
  2018-12-10 16:58   ` David Howells
@ 2018-12-10 18:04     ` Roberto Sassu
  0 siblings, 0 replies; 19+ messages in thread
From: Roberto Sassu @ 2018-12-10 18:04 UTC (permalink / raw)
  To: David Howells
  Cc: dwmw2, herbert, davem, keyrings, linux-crypto, linux-integrity,
	linux-kernel, silviu.vlasceanu

On 12/10/2018 5:58 PM, David Howells wrote:
> Roberto Sassu <roberto.sassu@huawei.com> wrote:
> 
>>> You need to consider what it is that the patch trying to achieve.
>>
>> I understood that the purpose is to check PGP signatures with built-in
>> keys or keys provided by the user. Since using the session keyring
>> caused the issue I reported, I thought it was ok to use the user
>> keyring.
>>
>> Just a note: the original patches were relying on KEY_FLAG_TRUSTED to
>> determine if a key is trusted; now the trustworthiness depends on the
>> type of keyring passed to pgp_verify_sig(). I removed the additional key
>> search in the user (session) keyring to prevent that signature
>> verification is done with a key provided by the user even when the
>> caller of pgp_verify_sig() expects that a trusted key is used. The
>> search in the session keyring is done if the caller of pgp_verify_sig()
>> sets the keyring pointer to NULL.
> 
> Thinking about these patches further, as you point out, the way that trust is
> computed has changed.  There is no no KEY_FLAG_TRUSTED; rather, a key is
> assumed to be trusted if it's on a trusted keyring.
> 
> *Getting* it onto that trusted keyring is then the trick.  There are two ways:
> 
>   (1) A key can be loaded from a trusted source during boot (say a compiled in
>       set of keys).
> 
>   (2) A key can be added to that keyring later, provided that the key is
>       verified by a key already in the ring and the ring hasn't been closed.
> 
> What do we need to check PGP signatures?  Blobs or keys as well?

For my use case, verifying blobs (RPM headers or 'Release' for Debian-
based distributions) would be sufficient. The keys can be added at
compile time.


> Why does the user keyring need to be a fallback?  (I know the session keyring
> used to be a fallback when I first did these, but things have changed since
> then).

Users can verify signatures with the pgp_test key type (patch 10/12).
During the first attempt, pgp_test tries to use built-in keys. If
verification fails, pgp_test tries again with keys in the user keyring.
But if verification succeeds during the second attempt, the kernel
prints a warning saying that an untrusted key was used.


> Should we have a separate built-in keyring for PGP keys?  Actually, I suspect
> we should probably mark keys in some way with what they're permitted to be
> used for.

I kept PGP keys in the main keyring, so that verify_pgp_signature() uses
the same convention of verify_pkcs7_signature() (keyring == NULL: use
primary keys, keyring == 1: use secondary keys).

In your patches there was a capability checking mechanism, but it was
removed with commit db6c43bd2132.

Roberto


> David
> 

-- 
HUAWEI TECHNOLOGIES Duesseldorf GmbH, HRB 56063
Managing Director: Bo PENG, Jian LI, Yanli SHI

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

end of thread, other threads:[~2018-12-10 18:05 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-12 10:24 [RFC][PATCH 00/12] keys: add support for PGP keys and signatures Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 01/12] mpi: introduce mpi_key_length() Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 02/12] rsa: add parser of raw format Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 03/12] PGPLIB: PGP definitions (RFC 4880) Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 04/12] PGPLIB: Basic packet parser Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 05/12] PGPLIB: Signature parser Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 06/12] KEYS: PGP data parser Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 07/12] KEYS: Provide PGP key description autogeneration Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 09/12] verification: introduce verify_pgp_signature() Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 10/12] PGP: Provide a key type for testing PGP signatures Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 11/12] KEYS: Provide a function to load keys from a PGP keyring blob Roberto Sassu
2018-11-12 10:24 ` [RFC][PATCH 12/12] KEYS: Introduce load_pgp_public_keyring() Roberto Sassu
2018-11-12 12:31 ` [RFC][PATCH 04/12] PGPLIB: Basic packet parser David Howells
2018-11-12 12:35 ` [RFC][PATCH 05/12] PGPLIB: Signature parser David Howells
2018-11-12 12:43 ` [RFC][PATCH 08/12] KEYS: PGP-based public key signature verification David Howells
2018-11-12 14:22   ` Roberto Sassu
2018-12-10 16:58   ` David Howells
2018-12-10 18:04     ` Roberto Sassu

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