diff mbox series

[RFC,08/12] KEYS: PGP-based public key signature verification

Message ID 20181112102423.30415-9-roberto.sassu@huawei.com (mailing list archive)
State New, archived
Headers show
Series keys: add support for PGP keys and signatures | expand

Commit Message

Roberto Sassu Nov. 12, 2018, 10:24 a.m. UTC
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

Comments

David Howells Nov. 12, 2018, 12:43 p.m. UTC | #1
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
Roberto Sassu Nov. 12, 2018, 2:22 p.m. UTC | #2
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
>
David Howells Dec. 10, 2018, 4:58 p.m. UTC | #3
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
Roberto Sassu Dec. 10, 2018, 6:04 p.m. UTC | #4
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
>
diff mbox series

Patch

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 */