mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: rusty@rustcorp.com.au, kyle@mcmartin.ca
Cc: linux-kernel@vger.kernel.org,
	linux-security-module@vger.kernel.org, keyrings@linux-nfs.org,
	David Howells <dhowells@redhat.com>
Subject: [PATCH 14/23] KEYS: PGP-based public key signature verification
Date: Wed, 23 May 2012 00:04:07 +0100	[thread overview]
Message-ID: <20120522230407.24007.81121.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <20120522230218.24007.3556.stgit@warthog.procyon.org.uk>

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.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 security/keys/crypto/Makefile         |    3 
 security/keys/crypto/pgp_parser.h     |    6 +
 security/keys/crypto/pgp_pubkey_sig.c |  324 +++++++++++++++++++++++++++++++++
 3 files changed, 332 insertions(+), 1 deletions(-)
 create mode 100644 security/keys/crypto/pgp_pubkey_sig.c


diff --git a/security/keys/crypto/Makefile b/security/keys/crypto/Makefile
index 3bb2e61..d4ac8ac 100644
--- a/security/keys/crypto/Makefile
+++ b/security/keys/crypto/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_PGP_LIBRARY) += pgp_library.o
 
 obj-$(CONFIG_CRYPTO_KEY_PGP_PARSER) += pgp_parser.o
 pgp_parser-y := \
-	pgp_key_parser.o
+	pgp_key_parser.o \
+	pgp_pubkey_sig.o
diff --git a/security/keys/crypto/pgp_parser.h b/security/keys/crypto/pgp_parser.h
index 1cda231..a6192ce 100644
--- a/security/keys/crypto/pgp_parser.h
+++ b/security/keys/crypto/pgp_parser.h
@@ -21,3 +21,9 @@
  */
 extern const
 struct public_key_algorithm *pgp_public_key_algorithms[PGP_PUBKEY__LAST];
+
+/*
+ * pgp_pubkey_sig.c
+ */
+extern struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
+	struct key *crypto_key, const u8 *sigdata, size_t siglen);
diff --git a/security/keys/crypto/pgp_pubkey_sig.c b/security/keys/crypto/pgp_pubkey_sig.c
new file mode 100644
index 0000000..bc02dfa
--- /dev/null
+++ b/security/keys/crypto/pgp_pubkey_sig.c
@@ -0,0 +1,324 @@
+/* Handling for PGP public key signature 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) "PGPSIG: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pgp.h>
+#include "public_key.h"
+#include "pgp_parser.h"
+
+const struct {
+	enum pkey_hash_algo algo : 8;
+} pgp_pubkey_hash[PGP_HASH__LAST] = {
+	[PGP_HASH_MD5].algo		= PKEY_HASH_MD5,
+	[PGP_HASH_SHA1].algo		= PKEY_HASH_SHA1,
+	[PGP_HASH_RIPE_MD_160].algo	= PKEY_HASH_RIPE_MD_160,
+	[PGP_HASH_SHA256].algo		= PKEY_HASH_SHA256,
+	[PGP_HASH_SHA384].algo		= PKEY_HASH_SHA384,
+	[PGP_HASH_SHA512].algo		= PKEY_HASH_SHA512,
+	[PGP_HASH_SHA224].algo		= PKEY_HASH_SHA224,
+};
+
+static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+					const void *data, size_t datalen);
+static int pgp_pkey_verify_sig_end(struct crypto_key_verify_context *ctx,
+				   const u8 *sig, size_t siglen);
+static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx);
+
+struct pgp_pkey_sig_parse_context {
+	struct pgp_parse_context pgp;
+	struct pgp_sig_parameters params;
+};
+
+static int pgp_pkey_parse_signature(struct pgp_parse_context *context,
+				    enum pgp_packet_tag type,
+				    u8 headerlen,
+				    const u8 *data,
+				    size_t datalen)
+{
+	struct pgp_pkey_sig_parse_context *ctx =
+		container_of(context, struct pgp_pkey_sig_parse_context, pgp);
+
+	return pgp_parse_sig_params(&data, &datalen, &ctx->params);
+}
+
+/*
+ * Begin the process of verifying a DSA signature.
+ *
+ * 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 the
+ * key.
+ */
+struct crypto_key_verify_context *pgp_pkey_verify_sig_begin(
+	struct key *crypto_key, const u8 *sigdata, size_t siglen)
+{
+	struct pgp_pkey_sig_parse_context p;
+	struct public_key_signature *sig;
+	struct crypto_shash *tfm;
+	const struct public_key *key = crypto_key->payload.data;
+	size_t digest_size, desc_size;
+	int ret;
+
+	kenter("{%d},,%zu", key_serial(crypto_key), siglen);
+
+	if (!key) {
+		kleave(" = -ENOKEY [no public key]");
+		return ERR_PTR(-ENOKEY);
+	}
+
+	p.pgp.types_of_interest = (1 << PGP_PKT_SIGNATURE);
+	p.pgp.process_packet = pgp_pkey_parse_signature;
+	ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	if (p.params.pubkey_algo >= PGP_PUBKEY__LAST ||
+	    !pgp_public_key_algorithms[p.params.pubkey_algo]) {
+		pr_debug("Unsupported public key algorithm %u\n",
+			 p.params.pubkey_algo);
+		return ERR_PTR(-ENOPKG);
+	}
+
+	if (pgp_public_key_algorithms[p.params.pubkey_algo] != key->algo) {
+		kleave(" = -EKEYREJECTED [wrong pk algo]");
+		return ERR_PTR(-EKEYREJECTED);
+	}
+
+	if (!(key->capabilities & PKEY_CAN_VERIFY)) {
+		kleave(" = -EKEYREJECTED [key can't verify]");
+		return ERR_PTR(-EKEYREJECTED);
+	}
+
+	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 (memcmp(&p.params.issuer, key->key_id, 8) != 0) {
+		kleave(" = -ENOKEY [wrong key ID]");
+		return ERR_PTR(-ENOKEY);
+	}
+
+	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);
+	}
+
+	/* 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))
+		return PTR_ERR(tfm) == -ENOENT ?
+			ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
+
+	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.
+	 */
+	sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
+	if (!sig) {
+		crypto_free_shash(tfm);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	sig->base.key		= crypto_key;
+	sig->base.add_data	= pgp_pkey_verify_sig_add_data;
+	sig->base.end		= pgp_pkey_verify_sig_end;
+	sig->base.cancel	= pgp_pkey_verify_sig_cancel;
+	sig->pkey_hash_algo	= pgp_pubkey_hash[p.params.hash_algo].algo;
+	sig->digest		= (u8 *)sig + sizeof(*sig) + desc_size;
+	sig->digest_size	= digest_size;
+	sig->hash.tfm		= tfm;
+	sig->hash.flags		= CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	ret = crypto_shash_init(&sig->hash);
+	if (ret < 0) {
+		crypto_free_shash(sig->hash.tfm);
+		kfree(sig);
+		return ERR_PTR(ret);
+	}
+
+	key_get(sig->base.key);
+	kleave(" = %p", sig);
+	return &sig->base;
+}
+
+/*
+ * Load data into the hash
+ */
+static int pgp_pkey_verify_sig_add_data(struct crypto_key_verify_context *ctx,
+					const void *data, size_t datalen)
+{
+	struct public_key_signature *sig =
+		container_of(ctx, struct public_key_signature, base);
+
+	return crypto_shash_update(&sig->hash, data, datalen);
+}
+
+struct pgp_pkey_sig_digest_context {
+	struct pgp_parse_context pgp;
+	const struct public_key *key;
+	struct public_key_signature *sig;
+};
+
+/*
+ * Extract required metadata from the signature packet and add what we need to
+ * to the hash.
+ */
+static int pgp_pkey_digest_signature(struct pgp_parse_context *context,
+				     enum pgp_packet_tag type,
+				     u8 headerlen,
+				     const u8 *data,
+				     size_t datalen)
+{
+	struct pgp_pkey_sig_digest_context *ctx =
+		container_of(context, struct pgp_pkey_sig_digest_context, pgp);
+	enum pgp_signature_version version;
+	int i;
+
+	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->sig->hash, data + 1, 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->sig->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->sig->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->sig->signed_hash_msw[0] = *data++;
+	ctx->sig->signed_hash_msw[1] = *data++;
+	datalen -= 2;
+
+	/* And then the cryptographic data, which we'll need for the
+	 * algorithm.
+	 */
+	for (i = 0; i < ctx->key->algo->n_sig_mpi; i++) {
+		unsigned int remaining = datalen;
+		if (remaining == 0) {
+			pr_debug("short %zu mpi %d\n", datalen, i);
+			return -EBADMSG;
+		}
+		ctx->sig->mpi[i] = mpi_read_from_buffer(data, &remaining);
+		if (!ctx->sig->mpi[i])
+			return -ENOMEM;
+		data += remaining;
+		datalen -= remaining;
+	}
+
+	if (datalen != 0) {
+		kleave(" = -EBADMSG [trailer %zu]", datalen);
+		return -EBADMSG;
+	}
+
+	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_pkey_verify_sig_end(struct crypto_key_verify_context *ctx,
+				   const u8 *sigdata, size_t siglen)
+{
+	struct public_key_signature *sig =
+		container_of(ctx, struct public_key_signature, base);
+	const struct public_key *key = sig->base.key->payload.data;
+	struct pgp_pkey_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_pkey_digest_signature;
+	p.key = key;
+	p.sig = sig;
+	ret = pgp_parse_packets(sigdata, siglen, &p.pgp);
+	if (ret < 0)
+		goto error_free_ctx;
+
+	crypto_shash_final(&sig->hash, sig->digest);
+
+	ret = key->algo->verify(key, sig);
+
+error_free_ctx:
+	pgp_pkey_verify_sig_cancel(ctx);
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Cancel an in-progress data loading
+ */
+static void pgp_pkey_verify_sig_cancel(struct crypto_key_verify_context *ctx)
+{
+	struct public_key_signature *sig =
+		container_of(ctx, struct public_key_signature, base);
+	int i;
+
+	kenter("");
+
+	/* !!! Do we need to tell the crypto layer to cancel too? */
+	crypto_free_shash(sig->hash.tfm);
+	key_put(sig->base.key);
+	for (i = 0; i < ARRAY_SIZE(sig->mpi); i++)
+		mpi_free(sig->mpi[i]);
+	kfree(sig);
+
+	kleave("");
+}


  parent reply	other threads:[~2012-05-22 23:04 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-05-22 23:02 [PATCH 00/23] Crypto keys and module signing David Howells
2012-05-22 23:02 ` [PATCH 01/23] Guard check in module loader against integer overflow David Howells
2012-05-22 23:02 ` [PATCH 02/23] KEYS: Move the key config into security/keys/Kconfig David Howells
2012-05-22 23:02 ` [PATCH 03/23] KEYS: Announce key type (un)registration David Howells
2012-05-22 23:02 ` [PATCH 04/23] KEYS: Reorganise keys Makefile David Howells
2012-05-22 23:02 ` [PATCH 05/23] KEYS: Create a key type that can be used for general cryptographic operations David Howells
2012-05-22 23:03 ` [PATCH 06/23] KEYS: Add signature verification facility David Howells
2012-05-22 23:03 ` [PATCH 07/23] KEYS: Asymmetric public-key algorithm crypto key subtype David Howells
2012-05-22 23:03 ` [PATCH 08/23] KEYS: RSA signature verification algorithm David Howells
2012-05-22 23:03 ` [PATCH 09/23] Fix signature verification for shorter signatures David Howells
2012-05-22 23:03 ` [PATCH 10/23] PGPLIB: PGP definitions (RFC 4880) David Howells
2012-05-22 23:03 ` [PATCH 11/23] PGPLIB: Basic packet parser David Howells
2012-05-22 23:03 ` [PATCH 12/23] PGPLIB: Signature parser David Howells
2012-05-22 23:03 ` [PATCH 13/23] KEYS: PGP data parser David Howells
2012-05-22 23:04 ` David Howells [this message]
2012-05-22 23:04 ` [PATCH 15/23] KEYS: PGP format signature parser David Howells
2012-05-22 23:04 ` [PATCH 16/23] KEYS: Provide a function to load keys from a PGP keyring blob David Howells
2012-05-22 23:04 ` [PATCH 17/23] MODSIGN: Provide gitignore and make clean rules for extra files David Howells
2012-05-22 23:04 ` [PATCH 18/23] MODSIGN: Provide Documentation and Kconfig options David Howells
2012-05-22 23:04 ` [PATCH 19/23] MODSIGN: Sign modules during the build process David Howells
2012-05-22 23:04 ` [PATCH 20/23] MODSIGN: Provide module signing public keys to the kernel David Howells
2012-05-22 23:05 ` [PATCH 21/23] MODSIGN: Module signature verification David Howells
2012-05-22 23:05 ` [PATCH 22/23] MODSIGN: Automatically generate module signing keys if missing David Howells
2012-05-22 23:05 ` [PATCH 23/23] MODSIGN: Panic the kernel if FIPS is enabled upon module signing failure David Howells
2012-05-23 12:51 ` [PATCH 00/23] Crypto keys and module signing Rusty Russell
2012-05-23 14:20 ` David Howells
2012-05-24 12:04   ` Rusty Russell
2012-05-25 11:15     ` Kasatkin, Dmitry
2012-05-25 11:37     ` David Howells
2012-05-25 13:08       ` Mimi Zohar
2012-05-25 13:53       ` David Howells
2012-05-25 14:40         ` Mimi Zohar
2012-05-24 14:00   ` David Howells
2012-05-27  5:41     ` Rusty Russell
2012-05-31 15:35       ` Josh Boyer
2012-06-04  1:16         ` Rusty Russell
2012-06-04 13:38           ` Josh Boyer
2012-06-05  0:23             ` Rusty Russell
2012-06-22  1:53       ` Greg KH
2012-06-22  3:29         ` Lucas De Marchi
2012-06-22  4:05         ` Rusty Russell
2012-06-22 11:03         ` David Howells
2012-06-23  0:20           ` Rusty Russell
2012-05-31 14:11     ` David Howells
2012-05-25 12:18 ` David Howells
2012-05-25 15:42 ` David Howells
2012-06-04  1:31   ` Rusty Russell
2012-06-04 12:47     ` Mimi Zohar
2012-06-05  1:05       ` Rusty Russell
2012-06-05 11:39         ` Mimi Zohar
2012-06-05 13:37         ` David Howells
2012-06-05 14:36           ` Kasatkin, Dmitry
2012-06-05 13:35   ` David Howells
2012-06-10  5:47     ` Rusty Russell
2012-06-11  8:30       ` Kasatkin, Dmitry

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=20120522230407.24007.81121.stgit@warthog.procyon.org.uk \
    --to=dhowells@redhat.com \
    --cc=keyrings@linux-nfs.org \
    --cc=kyle@mcmartin.ca \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=rusty@rustcorp.com.au \
    /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

Powered by JetHome