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