[PATCH 06/16] KEYS: Add a DSA crypto key subtype [ver #2]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a key subtype for handling DSA crypto keys.  For the moment it only
provides a signature verification facility.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

 security/Kconfig                   |   10 +
 security/keys/Makefile             |    2 
 security/keys/crypto_dsa.h         |   36 ++++
 security/keys/crypto_dsa_subtype.c |  338 ++++++++++++++++++++++++++++++++++++
 4 files changed, 386 insertions(+), 0 deletions(-)
 create mode 100644 security/keys/crypto_dsa.h
 create mode 100644 security/keys/crypto_dsa_subtype.c


diff --git a/security/Kconfig b/security/Kconfig
index ef39878..48926af 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -62,6 +62,16 @@ config CRYPTO_KEY_TYPE
 	  required for cryptographic operations such as encryption, decryption,
 	  signature generation and signature verification.
 
+config CRYPTO_KEY_DSA
+	tristate "DSA key type"
+	depends on CRYPTO_KEY_TYPE
+	select CRYPTO
+	select MPILIB
+	select MPILIB_EXTRA
+	help
+	  This option makes DSA cryptographic keys available.  They can be used
+	  for signature verification.
+
 config KEYS_DEBUG_PROC_KEYS
 	bool "Enable the /proc/keys file by which keys may be viewed"
 	depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index ca85b01..8c499b1 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -16,8 +16,10 @@ obj-y := \
 obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
 obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/
 obj-$(CONFIG_CRYPTO_KEY_TYPE) += crypto_keys.o
+obj-$(CONFIG_CRYPTO_KEY_DSA) += crypto_dsa.o
 obj-$(CONFIG_KEYS_COMPAT) += compat.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSCTL) += sysctl.o
 
 crypto_keys-y	:= crypto_type.o pgp_parse.o
+crypto_dsa-y	:= crypto_dsa_subtype.o
diff --git a/security/keys/crypto_dsa.h b/security/keys/crypto_dsa.h
new file mode 100644
index 0000000..0455634
--- /dev/null
+++ b/security/keys/crypto_dsa.h
@@ -0,0 +1,36 @@
+/* DSA internal definitions
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * 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 DSA_NPKEY	4	/* number of MPI's in DSA public key */
+
+extern struct crypto_key_subtype DSA_crypto_key_subtype;
+
+/*
+ * public key record
+ */
+struct DSA_public_key {
+	struct pgp_parse_pubkey pgp;
+	union {
+		MPI		pkey[DSA_NPKEY];
+		struct {
+			MPI	p;	/* DSA prime */
+			MPI	q;	/* DSA group order */
+			MPI	g;	/* DSA group generator */
+			MPI	y;	/* DSA public-key value = g^x mod p */
+		};
+	};
+};
+
+struct DSA_payload {
+	u8		key_id[8];	/* ID of this key pair */
+	u8		key_id_size;	/* Number of bytes in key_id */
+	struct DSA_public_key	*public_key;
+};
diff --git a/security/keys/crypto_dsa_subtype.c b/security/keys/crypto_dsa_subtype.c
new file mode 100644
index 0000000..ae6133b
--- /dev/null
+++ b/security/keys/crypto_dsa_subtype.c
@@ -0,0 +1,338 @@
+/* DSA crypto key subtype
+ *
+ * Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@xxxxxxxxxx)
+ *
+ * This file is derived from GnuPG.
+ *	Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#define DEBUG
+#define pr_fmt(fmt) "DSA: "fmt
+#include <keys/crypto-subtype.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mpi.h>
+#include <linux/pgp.h>
+#include <crypto/hash.h>
+#define __KDEBUG
+#include "internal.h"
+#include "crypto_dsa.h"
+
+MODULE_LICENSE("GPL");
+
+static inline void digest_putc(struct shash_desc *digest, uint8_t ch)
+{
+	crypto_shash_update(digest, &ch, 1);
+}
+
+/*
+ * Destroy the contents of a DSA payload
+ */
+static void DSA_destroy_payload(struct DSA_payload *dsa)
+{
+	int i;
+
+	if (dsa->public_key) {
+		for (i = 0; i < DSA_NPKEY; i++)
+			mpi_free(dsa->public_key->pkey[i]);
+		kfree(dsa->public_key);
+	}
+	kfree(dsa);
+}
+
+/*
+ * Calculate the public key ID (RFC4880 12.2)
+ */
+static void DSA_calc_pk_keyid(struct shash_desc *digest,
+			      struct DSA_public_key *pk)
+{
+	unsigned n;
+	unsigned nb[DSA_NPKEY];
+	unsigned nn[DSA_NPKEY];
+	u8 *pp[DSA_NPKEY];
+	u32 a32;
+	int i;
+	int npkey = DSA_NPKEY;
+
+	kenter("");
+
+	n = (pk->pgp.version < PGP_KEY_VERSION_4) ? 8 : 6;
+	for (i = 0; i < npkey; i++) {
+		nb[i] = mpi_get_nbits(pk->pkey[i]);
+		pp[i] = mpi_get_buffer(pk->pkey[i], nn + i, NULL);
+		n += 2 + nn[i];
+	}
+
+	digest_putc(digest, 0x99);     /* ctb */
+	digest_putc(digest, n >> 8);   /* 16-bit header length */
+	digest_putc(digest, n);
+	digest_putc(digest, pk->pgp.version);
+
+	a32 = pk->pgp.creation_time;
+	digest_putc(digest, a32 >> 24);
+	digest_putc(digest, a32 >> 16);
+	digest_putc(digest, a32 >>  8);
+	digest_putc(digest, a32 >>  0);
+
+	if (pk->pgp.version < PGP_KEY_VERSION_4) {
+		u16 a16;
+
+		if( pk->pgp.expires_at)
+			a16 = (pk->pgp.expires_at - pk->pgp.creation_time) / 86400UL;
+		else
+			a16 = 0;
+		digest_putc(digest, a16 >> 8);
+		digest_putc(digest, a16 >> 0);
+	}
+
+	digest_putc(digest, PGP_PUBKEY_DSA);
+
+	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]);
+		kfree(pp[i]);
+	}
+
+	kleave("");
+}
+
+/*
+ * Calculate and check the public key ID fingerprint against the key ID
+ */
+static int DSA_get_fingerprint(struct DSA_payload *dsa,
+			       struct DSA_public_key *pk,
+			       char **_fingerprint)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *digest;
+	int digest_size, offset;
+	char *fingerprint;
+	u8 *raw_fingerprint;
+	int ret, i;
+
+	ret = -ENOMEM;
+	tfm = crypto_alloc_shash(pk->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;
+
+	DSA_calc_pk_keyid(digest, pk);
+
+	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;
+	kdebug("offset %u/%u", offset, digest_size);
+
+	for (i = 0; i < digest_size; i++)
+		sprintf(fingerprint + i * 2, "%02x", raw_fingerprint[i]);
+	kdebug("fingerprint %s", fingerprint);
+
+	memcpy(&dsa->key_id, raw_fingerprint + offset, 8);
+	dsa->key_id_size = 8;
+
+	*_fingerprint = fingerprint;
+	fingerprint = NULL;
+	ret = 0;
+cleanup_raw_fingerprint:
+	kfree(raw_fingerprint);
+cleanup_hash:
+	kfree(digest);
+cleanup_tfm:
+	crypto_free_shash(tfm);
+cleanup:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+struct DSA_pk_parse_context {
+	struct pgp_parse_context pgp;
+	struct DSA_payload *dsa;
+	struct DSA_public_key *pk;
+	char *fingerprint;
+};
+
+/*
+ * Extract a public key or subkey from the PGP stream.
+ */
+static int DSA_parse_public_key(struct pgp_parse_context *context,
+				enum pgp_packet_tag type,
+				u8 headerlen,
+				const u8 *data,
+				size_t datalen)
+{
+	struct DSA_pk_parse_context *ctx =
+		container_of(context, struct DSA_pk_parse_context, pgp);
+	struct DSA_payload *dsa = ctx->dsa;
+	struct DSA_public_key *pk;
+	int i, ret;
+
+	kenter(",%u,%u,,%zu", type, headerlen, datalen);
+
+	if (dsa->public_key) {
+		kleave(" = -ENOKEY [already]");
+		return -ENOKEY;
+	}
+
+	pk = kzalloc(sizeof(struct DSA_public_key), GFP_KERNEL);
+	if (!pk)
+		return -ENOMEM;
+
+	ret = pgp_parse_public_key(&data, &datalen, &pk->pgp);
+	if (pk->pgp.pubkey_algo != PGP_PUBKEY_DSA) {
+		pr_debug("Ignoring non-DSA public key [%u]\n",
+			 pk->pgp.pubkey_algo);
+		ret = -ENOKEY;
+		goto cleanup;
+	}
+
+	ret = -ENOMEM;
+	for (i = 0; i < DSA_NPKEY; i++) {
+		unsigned int remaining = datalen;
+		pk->pkey[i] = mpi_read_from_buffer(data, &remaining);
+		if (!pk->pkey[i])
+			goto cleanup;
+		data += remaining;
+		datalen -= remaining;
+	}
+
+	ret = DSA_get_fingerprint(dsa, pk, &ctx->fingerprint);
+	if (ret < 0)
+		goto cleanup;
+
+	dsa->public_key = pk;
+	kleave(" = 0 [use]");
+	return 0;
+
+cleanup:
+	kdebug("cleanup");
+	if (pk) {
+		for (i = 0; i < DSA_NPKEY; i++)
+			mpi_free(pk->pkey[i]);
+		kfree(pk);
+	}
+	kleave(" = %d", ret);
+	return ret;
+}
+
+/*
+ * Instantiate a DSA key
+ */
+static int DSA_instantiate(struct key *key,
+			   const void *data, size_t datalen)
+{
+	struct DSA_pk_parse_context ctx;
+	struct DSA_payload *dsa;
+	int ret;
+
+	kenter("");
+
+	ret = key_payload_reserve(key, datalen);
+	if (ret < 0)
+		return ret;
+
+	dsa = kzalloc(sizeof(struct DSA_payload), GFP_KERNEL);
+	if (!dsa)
+		return -ENOMEM;
+
+	ctx.pgp.types_of_interest =
+		(1 << PGP_PKT_PUBLIC_KEY) | (1 << PGP_PKT_PUBLIC_SUBKEY);
+	ctx.pgp.process_packet = DSA_parse_public_key;
+	ctx.dsa = dsa;
+	ctx.pk = NULL;
+	ctx.fingerprint = NULL;
+
+	ret = pgp_parse_packets(data, datalen, &ctx.pgp);
+	if (ret < 0) {
+		DSA_destroy_payload(dsa);
+		kfree(ctx.fingerprint);
+		key_payload_reserve(key, 0);
+		return ret;
+	}
+
+	key->type_data.p[0] = &DSA_crypto_key_subtype;
+	key->type_data.p[1] = ctx.fingerprint;
+	key->payload.data = dsa;
+	return 0;
+}
+
+/*
+ * Destroy a DSA key
+ */
+static void DSA_destroy(struct key *key)
+{
+	if (key->payload.data)
+		DSA_destroy_payload(key->payload.data);
+}
+
+/*
+ * DSA crypto key subtype
+ */
+struct crypto_key_subtype DSA_crypto_key_subtype = {
+	.owner		= THIS_MODULE,
+	.name		= "dsa",
+	.pubkey_algo	= PGP_PUBKEY_DSA,
+	.info		= CRYPTO_KEY_IS_PUBKEY_ALGO,
+	.instantiate	= DSA_instantiate,
+	.destroy	= DSA_destroy,
+
+	.verify_sig_begin	= DSA_verify_sig_begin,
+	.verify_sig_add_data	= DSA_verify_sig_add_data,
+	.verify_sig_end		= DSA_verify_sig_end,
+	.verify_sig_cancel	= DSA_verify_sig_cancel,
+};
+
+/*
+ * Module stuff
+ */
+static int __init DSA_init(void)
+{
+	return register_crypto_key_subtype(&DSA_crypto_key_subtype);
+}
+
+static void __exit DSA_cleanup(void)
+{
+	unregister_crypto_key_subtype(&DSA_crypto_key_subtype);
+}
+
+module_init(DSA_init);
+module_exit(DSA_cleanup);

--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux