linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Roberto Sassu <roberto.sassu@huawei.com>
To: <dhowells@redhat.com>, <dwmw2@infradead.org>,
	<herbert@gondor.apana.org.au>, <davem@davemloft.net>
Cc: <keyrings@vger.kernel.org>, <linux-crypto@vger.kernel.org>,
	<linux-integrity@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<silviu.vlasceanu@huawei.com>
Subject: [RFC][PATCH 06/12] KEYS: PGP data parser
Date: Mon, 12 Nov 2018 11:24:17 +0100	[thread overview]
Message-ID: <20181112102423.30415-7-roberto.sassu@huawei.com> (raw)
In-Reply-To: <20181112102423.30415-1-roberto.sassu@huawei.com>

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


  parent reply	other threads:[~2018-11-12 10:30 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Roberto Sassu [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181112102423.30415-7-roberto.sassu@huawei.com \
    --to=roberto.sassu@huawei.com \
    --cc=davem@davemloft.net \
    --cc=dhowells@redhat.com \
    --cc=dwmw2@infradead.org \
    --cc=herbert@gondor.apana.org.au \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=silviu.vlasceanu@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).