2006-01-23 20:43:11

by David Härdeman

[permalink] [raw]
Subject: [PATCH 02/04] Add dsa crypto ops


Adds dsa cryptographic operations. Since a dsa signature is always two
160-bit integer, I've modeled the dsa crypto as a hash algorithm.

Signed-off-by: David H?rdeman <[email protected]>

--

Index: vanilla-kernel/crypto/Kconfig
===================================================================
--- vanilla-kernel.orig/crypto/Kconfig 2006-01-22 22:02:20.000000000 +0100
+++ vanilla-kernel/crypto/Kconfig 2006-01-22 22:08:27.000000000 +0100
@@ -342,6 +342,13 @@
Multiprecision maths library from GnuPG. Used for some
crypto algorithms.

+config CRYPTO_DSA
+ tristate "Digital Signature Algorithm (EXPERIMENTAL)"
+ depends on CRYPTO && CRYPTO_MPILIB && EXPERIMENTAL
+ help
+ Digital Signature Algorithm is used in a number of applications
+ such as ssh and gpg.
+
config CRYPTO_TEST
tristate "Testing module"
depends on CRYPTO
Index: vanilla-kernel/crypto/Makefile
===================================================================
--- vanilla-kernel.orig/crypto/Makefile 2006-01-22 22:02:37.000000000 +0100
+++ vanilla-kernel/crypto/Makefile 2006-01-22 22:08:27.000000000 +0100
@@ -30,6 +30,7 @@
obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o
+obj-$(CONFIG_CRYPTO_DSA) += dsa.o
obj-$(CONFIG_CRYPTO_MPILIB) += mpi/

obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
Index: vanilla-kernel/crypto/dsa.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ vanilla-kernel/crypto/dsa.c 2006-01-22 22:08:11.000000000 +0100
@@ -0,0 +1,230 @@
+/*
+ * DSA Digital Signature Algorithm (FIPS-186).
+ *
+ * Copyright (c) 2005 David H?rdeman <[email protected]>
+ *
+ * This program 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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/mpi.h>
+#include <linux/dsa.h>
+#include <linux/random.h>
+#include "../security/keys/internal.h"
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <linux/scatterlist.h>
+
+
+/****************
+ * Generate a random secret exponent k less than q
+ */
+static MPI
+gen_k(MPI q)
+{
+ MPI k = mpi_alloc(mpi_get_nlimbs(q));
+ unsigned int nbits = mpi_get_nbits(q);
+ unsigned int nbytes = (nbits + 7)/8;
+ char *rndbuf = NULL;
+
+ dprintk("dsa: choosing a random k\n");
+
+ while(1) {
+ if (!rndbuf) {
+ rndbuf = kmalloc(nbytes, GFP_KERNEL);
+ if (!rndbuf) {
+ printk("dsa: failed to create buffer\n");
+ return NULL;
+ }
+ get_random_bytes(rndbuf, nbytes);
+ } else {
+ /* change only some of the higher bits */
+ get_random_bytes(rndbuf, min(nbytes, (unsigned int)4));
+ }
+
+ mpi_set_buffer(k, rndbuf, nbytes, 0);
+ if(mpi_test_bit( k, nbits - 1)) {
+ mpi_set_highbit(k, nbits - 1);
+ } else {
+ mpi_set_highbit(k, nbits - 1);
+ mpi_clear_bit(k, nbits - 1);
+ }
+
+ /* check: k < q */
+ if(!(mpi_cmp(k, q) < 0))
+ continue;
+
+ /* check: k > 0 */
+ if(!(mpi_cmp_ui(k, 0) > 0))
+ continue;
+
+ /* okay */
+ break;
+ }
+
+ kfree(rndbuf);
+ return k;
+}
+
+static void
+sign_hash(MPI r, MPI s, MPI hash, struct key_payload_dsa *skey)
+{
+ MPI k, kinv, tmp;
+
+ /* select a random k with 0 < k < q */
+ k = gen_k(skey->part[DSA_PART_Q]);
+ if (!k) {
+ printk("dsa: failed to create buffer\n");
+ return;
+ }
+
+ /* r = (g^k mod p) mod q */
+ mpi_powm(r, skey->part[DSA_PART_G], k, skey->part[DSA_PART_P]);
+ mpi_fdiv_r(r, r, skey->part[DSA_PART_Q]);
+
+ /* kinv = k^(-1) mod q */
+ kinv = mpi_alloc(mpi_get_nlimbs(k));
+ mpi_invm(kinv, k, skey->part[DSA_PART_Q]);
+
+ /* s = (kinv * ( hash + x * r)) mod q */
+ tmp = mpi_alloc(mpi_get_nlimbs(skey->part[DSA_PART_P]));
+ mpi_mul(tmp, skey->part[DSA_PART_X], r);
+ mpi_add(tmp, tmp, hash);
+ mpi_mulm(s , kinv, tmp, skey->part[DSA_PART_Q]);
+
+ mpi_free(k);
+ mpi_free(kinv);
+ mpi_free(tmp);
+}
+
+struct dsa_ctx {
+ struct crypto_tfm *sha1;
+ struct key_payload_dsa *key;
+};
+
+static int dsa_setkey(void *ctx, const u8 *key, unsigned int keylen, u32 *flags)
+{
+ struct dsa_ctx *dctx = ctx;
+
+ if (keylen != sizeof(struct key_payload_dsa *)) {
+ printk("Invalid key size in dsa_setkey\n");
+ return -EINVAL;
+ }
+
+ dctx->key = (struct key_payload_dsa *)key;
+ return 0;
+}
+
+static void dsa_init(void *ctx)
+{
+ struct dsa_ctx *dctx = ctx;
+
+ dctx->key = NULL;
+ dctx->sha1 = crypto_alloc_tfm("sha1", 0);
+ if (!dctx->sha1)
+ printk("dsa_init: failed to allocate sha1 tfm\n");
+ else
+ crypto_digest_init(dctx->sha1);
+}
+
+static void dsa_update(void *ctx, const u8 *data, unsigned int dlen)
+{
+ struct scatterlist sg[1];
+ struct dsa_ctx *dctx = ctx;
+
+ if (!dctx->sha1)
+ return;
+
+ sg_init_one(sg, (u8 *)data, dlen);
+ crypto_digest_update(dctx->sha1, sg, 1);
+}
+
+static void dsa_final(void *ctx, u8 *out)
+{
+ struct dsa_ctx *dctx = ctx;
+ unsigned int dsize = crypto_tfm_alg_digestsize(dctx->sha1);
+ u8 buffer[dsize];
+ MPI hash, r, s;
+ u8 *outp = out;
+ unsigned int rbytes, rbits, sbytes, sbits;
+ char *rbuf, *sbuf;
+
+ if (!dctx->sha1)
+ return;
+
+ crypto_digest_final(dctx->sha1, buffer);
+ crypto_free_tfm(dctx->sha1);
+ dctx->sha1 = NULL;
+
+ hash = mpi_alloc(1);
+ r = mpi_alloc(1);
+ s = mpi_alloc(1);
+ if (!hash || !r || !s) {
+ printk("dsa_final: failed to allocate mpis\n");
+ goto out1;
+ }
+
+ mpi_set_buffer(hash, buffer, dsize, 0);
+ sign_hash(r, s, hash, dctx->key);
+ rbuf = mpi_get_buffer(r, &rbytes, NULL);
+ sbuf = mpi_get_buffer(s, &sbytes, NULL);
+ if (!rbuf || !sbuf) {
+ printk("dsa_final: failed to allocate buffers\n");
+ goto out2;
+ }
+
+ rbits = mpi_get_nbits(r);
+ MPI_WSIZE(outp, rbits);
+ memcpy(outp, rbuf, rbytes);
+ outp += rbytes;
+
+ sbits = mpi_get_nbits(s);
+ MPI_WSIZE(outp, sbits);
+ memcpy(outp, sbuf, sbytes);
+
+out2:
+ kfree(rbuf);
+ kfree(sbuf);
+out1:
+ mpi_free(hash);
+ mpi_free(r);
+ mpi_free(s);
+}
+
+static struct crypto_alg alg = {
+ .cra_name = "dsa",
+ .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
+ .cra_blocksize = DSA_HMAC_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct dsa_ctx),
+ .cra_module = THIS_MODULE,
+ .cra_list = LIST_HEAD_INIT(alg.cra_list),
+ .cra_u = { .digest = {
+ .dia_digestsize = DSA_DIGEST_SIZE,
+ .dia_init = dsa_init,
+ .dia_update = dsa_update,
+ .dia_final = dsa_final,
+ .dia_setkey = dsa_setkey } }
+};
+
+static int __init init(void)
+{
+ return crypto_register_alg(&alg);
+}
+
+static void __exit fini(void)
+{
+ crypto_unregister_alg(&alg);
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_AUTHOR("David H?rdeman");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DSA Digital Signature Algorithm");
Index: vanilla-kernel/include/linux/dsa.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ vanilla-kernel/include/linux/dsa.h 2006-01-22 22:07:58.000000000 +0100
@@ -0,0 +1,39 @@
+/* dsa.h: digital signature architecture
+ *
+ * Copyright (C) 2005 David H?rdeman ([email protected]).
+ * All Rights Reserved.
+ *
+ * This program 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.
+ */
+
+#ifndef _LINUX_DSA_H
+#define _LINUX_DSA_H
+#ifdef __KERNEL__
+
+#include <linux/mpi.h>
+
+#define DSA_DIGEST_SIZE 44
+#define DSA_HMAC_BLOCK_SIZE 64
+#define DSA_PART_P 0
+#define DSA_PART_Q 1
+#define DSA_PART_G 2
+#define DSA_PART_Y 3
+#define DSA_PART_X 4
+#define DSA_PARTS 5
+#define DSA_PUBLIC_PARTS 4
+
+struct key_payload_dsa {
+ MPI part[DSA_PARTS]; /* p,q,g,y,x */
+};
+
+#if 0
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...) do { } while(0)
+#endif
+
+#endif
+#endif


2006-01-23 20:43:35

by David Härdeman

[permalink] [raw]
Subject: [PATCH 04/04] Add dsa key type


Adds the dsa in-kernel key type.

Signed-off-by: David H?rdeman <[email protected]>

--

Index: vanilla-kernel/security/Kconfig
===================================================================
--- vanilla-kernel.orig/security/Kconfig 2006-01-17 22:49:50.000000000 +0100
+++ vanilla-kernel/security/Kconfig 2006-01-23 18:45:29.000000000 +0100
@@ -21,6 +21,14 @@

If you are unsure as to whether this is required, answer N.

+config KEYS_DSA_KEYS
+ tristate "Support DSA keys (EXPERIMENTAL)"
+ depends on KEYS && EXPERIMENTAL
+ select CRYPTO_MPILIB
+ select CRYPTO_DSA
+ help
+ This option provides support for retaining DSA keys in the kernel.
+
config KEYS_DEBUG_PROC_KEYS
bool "Enable the /proc/keys file by which all keys may be viewed"
depends on KEYS
Index: vanilla-kernel/security/keys/Makefile
===================================================================
--- vanilla-kernel.orig/security/keys/Makefile 2006-01-15 18:22:51.000000000 +0100
+++ vanilla-kernel/security/keys/Makefile 2006-01-23 18:46:00.000000000 +0100
@@ -13,4 +13,5 @@
user_defined.o

obj-$(CONFIG_KEYS_COMPAT) += compat.o
+obj-$(CONFIG_KEYS_DSA_KEYS) += dsa_key.o
obj-$(CONFIG_PROC_FS) += proc.o
Index: vanilla-kernel/security/keys/dsa_key.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ vanilla-kernel/security/keys/dsa_key.c 2006-01-23 18:46:00.000000000 +0100
@@ -0,0 +1,372 @@
+/* dsa_key.c: DSA key
+ *
+ * Copyright (C) 2005 David H?rdeman ([email protected]). All Rights Reserved.
+ *
+ * This program 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include <linux/mpi.h>
+#include <linux/dsa.h>
+#include <linux/random.h>
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+static int dsa_instantiate(struct key *key, const void *data, size_t datalen);
+static int dsa_update(struct key *key, const void *data, size_t datalen);
+static void dsa_destroy(struct key *key);
+static long dsa_read(const struct key *key,
+ char __user *buffer, size_t buflen);
+static long dsa_encrypt(const struct key *key,
+ char __user *_data, size_t dlen,
+ char __user *_result, size_t rlen);
+
+static struct key_type key_type_dsa = {
+ .name = "dsa",
+ .instantiate = dsa_instantiate,
+ .update = dsa_update,
+ .destroy = dsa_destroy,
+ .read = dsa_read,
+ .encrypt = dsa_encrypt,
+ .match = user_match,
+ .describe = user_describe,
+};
+
+/****************
+ * Signs a chunk of data using key.
+ * Returns: signature.
+ */
+static char *
+sign(const struct key_payload_dsa *skey,
+ const void *data, size_t datalen, unsigned int *rlen)
+{
+ struct crypto_tfm *tfm;
+ char *ret = NULL;
+ u8 *sig;
+ unsigned int sigsize;
+ int i;
+ struct scatterlist sg[1];
+
+ dprintk("dsa: entering sign\n");
+ tfm = crypto_alloc_tfm("dsa", 0);
+ if (!tfm)
+ goto out;
+
+ sigsize = crypto_tfm_alg_digestsize(tfm);
+ sig = kmalloc(sigsize, GFP_KERNEL);
+ if (!sig)
+ goto out_tfm;
+
+ crypto_digest_init(tfm);
+ i = crypto_digest_setkey(tfm, (const u8 *)skey, sizeof(skey));
+ if (i) {
+ printk("dsa: crypto_digest_setkey failed with error %i\n", i);
+ goto out_sig;
+ }
+
+ sg_init_one(sg, (u8 *)data, datalen);
+ crypto_digest_update(tfm, sg, 1);
+ crypto_digest_final(tfm, sig);
+ ret = sig;
+ *rlen = sigsize;
+ goto out_tfm;
+
+out_sig:
+ kfree(sig);
+out_tfm:
+ crypto_free_tfm(tfm);
+out:
+ return ret;
+}
+
+/****************
+ * Test whether the secret key is valid.
+ * Returns: if this is a valid key.
+ */
+static int check_secret_key(struct key_payload_dsa *skey)
+{
+ int rc;
+ MPI y;
+
+ /* y = g^x mod p */
+ dprintk("dsa: In check secret\n");
+ y = mpi_alloc(mpi_get_nlimbs(skey->part[DSA_PART_Y]));
+ rc = mpi_powm(y, skey->part[DSA_PART_G], skey->part[DSA_PART_X], skey->part[DSA_PART_P]);
+ rc = !mpi_cmp(y, skey->part[DSA_PART_Y]);
+
+ mpi_free(y);
+ return rc;
+}
+
+/*****************************************************************************/
+/*
+ * create a dsa key payload
+ */
+static int dsa_create_payload(struct key_payload_dsa **payload, struct key *key,
+ const void *data, size_t datalen)
+{
+ int ret, i;
+ unsigned int remain, read;
+ const unsigned char *ptr;
+
+ dprintk("dsa: entering dsa_create_payload\n");
+
+ ret = -EINVAL;
+ if (datalen <= 0 || datalen > 32767 || !data)
+ goto out1;
+
+ ret = -ENOMEM;
+ *payload = kmalloc(sizeof(struct key_payload_dsa), GFP_KERNEL);
+ if (!(*payload))
+ goto out1;
+
+ ret = key_payload_reserve(key, datalen);
+ if (ret < 0)
+ goto out2;
+
+ /* read each mpi number from buffer */
+ remain = read = (unsigned int)datalen;
+ ptr = data;
+ ret = 0;
+ for (i = 0; i < DSA_PARTS; i++) {
+ dprintk("dsa: in loop %i, remain is %u\n", i, remain);
+ (*payload)->part[i] = mpi_read_from_buffer(ptr, &read);
+ if (!((*payload)->part[i]))
+ ret = -EINVAL;
+ ptr += read;
+ remain -= read;
+ read = remain;
+ dprintk("dsa: end loop\n");
+ }
+
+ if (ret < 0)
+ goto out3;
+
+ /* check validity */
+ ret = -EINVAL;
+ if(check_secret_key(*payload)) {
+ ret = 0;
+ dprintk("dsa: valid\n");
+ goto out1;
+ }
+
+out3:
+ printk("dsa: attempt to add invalid key\n");
+ for (i = 0; i < DSA_PARTS; i++)
+ mpi_free((*payload)->part[i]);
+out2:
+ kfree(*payload);
+out1:
+ return ret;
+
+} /* end dsa_instantiate() */
+
+/*****************************************************************************/
+/*
+ * instantiate a dsa key
+ */
+static int dsa_instantiate(struct key *key, const void *data, size_t datalen)
+{
+ struct key_payload_dsa *dsa_key;
+ int ret;
+
+ dprintk("dsa: entering dsa_instantiate\n");
+ ret = dsa_create_payload(&dsa_key, key, data, datalen);
+ if (ret == 0)
+ key->payload.data = dsa_key;
+
+ return ret;
+
+} /* end dsa_instantiate() */
+
+/*****************************************************************************/
+/*
+ * update a user defined key
+ */
+static int dsa_update(struct key *key, const void *data, size_t datalen)
+{
+ struct key_payload_dsa *new_payload;
+ int ret;
+
+ dprintk("dsa: entering dsa_update\n");
+ ret = dsa_create_payload(&new_payload, key, data, datalen);
+ if (ret == 0) {
+ dprintk("dsa: dsa_create_payload success\n");
+ /* this destroys the old payload, not the entire key */
+ dsa_destroy(key);
+ key->payload.data = new_payload;
+ key->expiry = 0;
+ }
+
+ return ret;
+
+} /* end dsa_update() */
+
+/*****************************************************************************/
+/*
+ * dispose of the key payload
+ */
+static void dsa_destroy(struct key *key)
+{
+ struct key_payload_dsa *dsa_key;
+ int i;
+
+ dprintk("dsa: entering dsa_destroy\n");
+ dsa_key = key->payload.data;
+ if (dsa_key) {
+ for (i = 0; i < DSA_PARTS; i++)
+ mpi_free(dsa_key->part[i]);
+ kfree(dsa_key);
+ }
+
+} /* end dsa_destroy() */
+
+/*****************************************************************************/
+/*
+ * read the key data
+ */
+static long dsa_read(const struct key *key, char __user *buffer, size_t buflen)
+{
+ unsigned char *xbuffer[DSA_PUBLIC_PARTS];
+ unsigned nbytes[DSA_PUBLIC_PARTS];
+ unsigned nbits[DSA_PUBLIC_PARTS];
+ struct key_payload_dsa *dsa_key;
+ int i;
+ char *result, *tmp;
+ size_t reslen;
+ long ret;
+
+ dprintk("dsa: entering dsa_read\n");
+ ret = -EINVAL;
+ dsa_key = (struct key_payload_dsa *)key->payload.data;
+ if (!dsa_key)
+ goto out1;
+
+ /* 4 x 2 bytes to store mpi sizes */
+ reslen = 8;
+ memset(xbuffer, 0, sizeof(xbuffer));
+ ret = -ENOMEM;
+
+ for (i = 0; i < DSA_PUBLIC_PARTS; i++) {
+ xbuffer[i] = mpi_get_buffer(dsa_key->part[i], &nbytes[i], NULL);
+ if (!xbuffer[i]) {
+ dprintk("dsa: failed to get buffer\n");
+ goto out2;
+ }
+ reslen += nbytes[i];
+ nbits[i] = mpi_get_nbits(dsa_key->part[i]);
+ }
+
+ result = kmalloc(reslen, GFP_KERNEL);
+ if (!result)
+ goto out2;
+
+ tmp = result;
+ for (i = 0; i < DSA_PUBLIC_PARTS; i++) {
+ dprintk("dsa: checking part %i\n", i);
+ dprintk("dsa: nbytes is %i, nbits is %i, topbyte is %2hx, botbyte is %2hx\n",
+ nbytes[i], nbits[i], ((nbits[i] >> 8) & 0xff), ((nbits[i]) & 0xff));
+ MPI_WSIZE(tmp, nbits[i]);
+ memcpy(tmp, xbuffer[i], nbytes[i]);
+ tmp += nbytes[i];
+ }
+
+ ret = -EFAULT;
+ if (copy_to_user(buffer, result, min(reslen, buflen)) == 0)
+ ret = reslen;
+
+ memset(result, 0, reslen);
+ kfree(result);
+
+out2:
+ for (i = 0; i < DSA_PUBLIC_PARTS; i++) {
+ memset(xbuffer[i], 0, nbytes[i]);
+ kfree(xbuffer[i]);
+ }
+out1:
+ dprintk("dsa: leving dsa_read\n");
+ return ret;
+
+} /* end dsa_read() */
+
+/*****************************************************************************/
+/*
+ * encrypt data using a key
+ */
+static long dsa_encrypt(const struct key *key,
+ char __user *data, size_t dlen,
+ char __user *result, size_t rlen)
+{
+ char *signature;
+ char *inbuf;
+ long ret;
+ size_t siglen = 0;
+
+ dprintk("dsa: entering dsa_encrypt\n");
+ ret = -ENOMEM;
+ inbuf = kmalloc(dlen, GFP_KERNEL);
+ if (!inbuf)
+ goto out1;
+
+ ret = -EFAULT;
+ /* pull the data to sign into kernel space */
+ if (copy_from_user(inbuf, data, dlen))
+ goto out2;
+
+ /* sign it */
+ signature = sign((struct key_payload_dsa *)key->payload.data,
+ inbuf, dlen, &siglen);
+ if (!signature)
+ goto out2;
+
+ /* push the result to userspace */
+ if(copy_to_user(result, signature, min(rlen, siglen)) == 0)
+ ret = siglen;
+
+ memset(signature, 0, siglen);
+ kfree(signature);
+out2:
+ memset(inbuf, 0, dlen);
+ kfree(inbuf);
+out1:
+ return ret;
+} /* end dsa_encrypt() */
+
+static int __init dsa_init(void)
+{
+ int ret;
+
+ ret = register_key_type(&key_type_dsa);
+ if (ret < 0)
+ printk("dsa_key: failed to register key type\n");
+ else
+ printk("dsa_key: new key type registered\n");
+
+ return ret;
+}
+
+static void __exit dsa_exit (void)
+{
+ printk("dsa_key: unregistering key type\n");
+ unregister_key_type(&key_type_dsa);
+}
+
+module_init(dsa_init);
+module_exit(dsa_exit);
+
+MODULE_AUTHOR("David H?rdeman");
+MODULE_DESCRIPTION("DSA key support");
+MODULE_LICENSE("GPL");

2006-01-23 20:43:37

by David Härdeman

[permalink] [raw]
Subject: [PATCH 03/04] Add encryption ops to the keyctl syscall


Changes the keyctl syscall to accept six arguments (is it valid to do so?)
and adds encryption as one of the supported ops for in-kernel keys.

Signed-off-by: David H?rdeman <[email protected]>

--

Index: vanilla-kernel/include/linux/compat.h
===================================================================
--- vanilla-kernel.orig/include/linux/compat.h 2006-01-15 18:22:51.000000000 +0100
+++ vanilla-kernel/include/linux/compat.h 2006-01-23 18:40:52.000000000 +0100
@@ -132,8 +132,8 @@
long compat_sys_shmctl(int first, int second, void __user *uptr);
long compat_sys_semtimedop(int semid, struct sembuf __user *tsems,
unsigned nsems, const struct compat_timespec __user *timeout);
-asmlinkage long compat_sys_keyctl(u32 option,
- u32 arg2, u32 arg3, u32 arg4, u32 arg5);
+asmlinkage long compat_sys_keyctl(u32 option, u32 arg2, u32 arg3,
+ u32 arg4, u32 arg5, u32 arg6);

asmlinkage ssize_t compat_sys_readv(unsigned long fd,
const struct compat_iovec __user *vec, unsigned long vlen);
Index: vanilla-kernel/include/linux/key.h
===================================================================
--- vanilla-kernel.orig/include/linux/key.h 2006-01-17 22:49:50.000000000 +0100
+++ vanilla-kernel/include/linux/key.h 2006-01-23 18:40:52.000000000 +0100
@@ -220,6 +220,17 @@
*/
long (*read)(const struct key *key, char __user *buffer, size_t buflen);

+ /* encrypt data using a key (optional)
+ * - permission checks will be done by the caller
+ * - the key's semaphore will be readlocked by the caller
+ * - should return the amount of data that would be returned from the
+ * encryption even if the buffer is NULL
+ * - expects the output buffer to be able to hold the result
+ */
+ long (*encrypt)(const struct key *key,
+ char __user *inbuffer, size_t inbuflen,
+ char __user *outbuffer, size_t outbuflen);
+
/* handle request_key() for this type instead of invoking
* /sbin/request-key (optional)
* - key is the key to instantiate
Index: vanilla-kernel/include/linux/keyctl.h
===================================================================
--- vanilla-kernel.orig/include/linux/keyctl.h 2006-01-17 22:49:50.000000000 +0100
+++ vanilla-kernel/include/linux/keyctl.h 2006-01-23 18:40:52.000000000 +0100
@@ -49,5 +49,6 @@
#define KEYCTL_SET_REQKEY_KEYRING 14 /* set default request-key keyring */
#define KEYCTL_SET_TIMEOUT 15 /* set key timeout */
#define KEYCTL_ASSUME_AUTHORITY 16 /* assume request_key() authorisation */
+#define KEYCTL_ENCRYPT 17 /* encrypt a chunk of data using key */

#endif /* _LINUX_KEYCTL_H */
Index: vanilla-kernel/include/linux/syscalls.h
===================================================================
--- vanilla-kernel.orig/include/linux/syscalls.h 2006-01-17 22:49:50.000000000 +0100
+++ vanilla-kernel/include/linux/syscalls.h 2006-01-23 18:40:52.000000000 +0100
@@ -504,8 +504,9 @@
const char __user *_callout_info,
key_serial_t destringid);

-asmlinkage long sys_keyctl(int cmd, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5);
+asmlinkage long sys_keyctl(int cmd, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+ unsigned long arg5, unsigned long arg6);

asmlinkage long sys_ioprio_set(int which, int who, int ioprio);
asmlinkage long sys_ioprio_get(int which, int who);
Index: vanilla-kernel/security/keys/compat.c
===================================================================
--- vanilla-kernel.orig/security/keys/compat.c 2006-01-17 22:49:50.000000000 +0100
+++ vanilla-kernel/security/keys/compat.c 2006-01-23 18:44:01.000000000 +0100
@@ -23,8 +23,8 @@
* registers on taking a 32-bit syscall are zero
* - if you can, you should call sys_keyctl directly
*/
-asmlinkage long compat_sys_keyctl(u32 option,
- u32 arg2, u32 arg3, u32 arg4, u32 arg5)
+asmlinkage long compat_sys_keyctl(u32 option, u32 arg2, u32 arg3,
+ u32 arg4, u32 arg5, u32 arg6)
{
switch (option) {
case KEYCTL_GET_KEYRING_ID:
@@ -80,6 +80,11 @@
case KEYCTL_ASSUME_AUTHORITY:
return keyctl_assume_authority(arg2);

+ case KEYCTL_ENCRYPT:
+ return keyctl_encrypt_with_key(arg2,
+ compat_ptr(arg3), arg4,
+ compat_ptr(arg5), arg6);
+
default:
return -EOPNOTSUPP;
}
Index: vanilla-kernel/security/keys/keyctl.c
===================================================================
--- vanilla-kernel.orig/security/keys/keyctl.c 2006-01-17 22:49:50.000000000 +0100
+++ vanilla-kernel/security/keys/keyctl.c 2006-01-23 18:44:02.000000000 +0100
@@ -1066,10 +1066,71 @@

/*****************************************************************************/
/*
+ * encrypt a chunk of data using a specified key
+ * - implements keyctl(KEYCTL_ENCRYPT)
+ */
+long keyctl_encrypt_with_key(key_serial_t keyid,
+ const void __user *data,
+ size_t dlen,
+ const void __user *result,
+ size_t rlen)
+{
+ struct key *key;
+ key_ref_t key_ref;
+ long ret;
+
+ /* find the key first */
+ key_ref = lookup_user_key(NULL, keyid, 0, 0, 0);
+ if (IS_ERR(key_ref)) {
+ ret = -ENOKEY;
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ /* see if we can read it directly */
+ ret = key_permission(key_ref, KEY_READ);
+ if (ret == 0)
+ goto can_read_key;
+ if (ret != -EACCES)
+ goto error;
+
+ /* we can't; see if it's searchable from this process's keyrings
+ * - we automatically take account of the fact that it may be
+ * dangling off an instantiation key
+ */
+ if (!is_key_possessed(key_ref)) {
+ ret = -EACCES;
+ goto error2;
+ }
+
+ /* the key is probably readable - now try to read it */
+ can_read_key:
+ ret = key_validate(key);
+ if (ret == 0) {
+ ret = -EOPNOTSUPP;
+ if (key->type->encrypt) {
+ /* encrypt the data with the semaphore held (since we
+ * might sleep) */
+ down_read(&key->sem);
+ ret = key->type->encrypt(key, data, dlen, result, rlen);
+ up_read(&key->sem);
+ }
+ }
+
+ error2:
+ key_put(key);
+ error:
+ return ret;
+} /* end keyctl_encrypt_with_key() */
+
+/*****************************************************************************/
+/*
* the key control system call
*/
-asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5)
+asmlinkage long sys_keyctl(int option, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+ unsigned long arg5, unsigned long arg6)
{
switch (option) {
case KEYCTL_GET_KEYRING_ID:
@@ -1144,6 +1205,13 @@
case KEYCTL_ASSUME_AUTHORITY:
return keyctl_assume_authority((key_serial_t) arg2);

+ case KEYCTL_ENCRYPT:
+ return keyctl_encrypt_with_key((key_serial_t) arg2,
+ (const char __user *) arg3,
+ (size_t) arg4,
+ (const char __user *) arg5,
+ (size_t) arg6);
+
default:
return -EOPNOTSUPP;
}

2006-01-24 01:22:11

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 02/04] Add dsa crypto ops

David H?rdeman <[email protected]> wrote:
>
> +static int dsa_setkey(void *ctx, const u8 *key, unsigned int keylen, u32 *flags)
> +{
> + struct dsa_ctx *dctx = ctx;
> +
> + if (keylen != sizeof(struct key_payload_dsa *)) {
> + printk("Invalid key size in dsa_setkey\n");
> + return -EINVAL;
> + }
> +
> + dctx->key = (struct key_payload_dsa *)key;
> + return 0;
> +}

This is bad. You're putting a pointer to an object with an unknown
lifetime into the tfm.

Is there anything wrong with allocating the memory for it and storing
the key in the tfm like everyone else?

Cheers,
--
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2006-01-24 04:32:52

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 03/04] Add encryption ops to the keyctl syscall

On Mon, 23 Jan 2006 21:42:32 +0100 David H?rdeman wrote:

>
> Changes the keyctl syscall to accept six arguments (is it valid to do so?)
> and adds encryption as one of the supported ops for in-kernel keys.

Does this say anything about the amount of syscall testing
that this code has had?

---
~Randy

2006-01-24 06:49:54

by David Härdeman

[permalink] [raw]
Subject: Re: [PATCH 02/04] Add dsa crypto ops

On Tue, Jan 24, 2006 at 12:22:02PM +1100, Herbert Xu wrote:
>David H?rdeman <[email protected]> wrote:
>>
>> +static int dsa_setkey(void *ctx, const u8 *key, unsigned int keylen, u32 *flags)
>> +{
>> + struct dsa_ctx *dctx = ctx;
>> +
>> + if (keylen != sizeof(struct key_payload_dsa *)) {
>> + printk("Invalid key size in dsa_setkey\n");
>> + return -EINVAL;
>> + }
>> +
>> + dctx->key = (struct key_payload_dsa *)key;
>> + return 0;
>> +}
>
>This is bad. You're putting a pointer to an object with an unknown
>lifetime into the tfm.
>
>Is there anything wrong with allocating the memory for it and storing
>the key in the tfm like everyone else?

No, not at all, it's a mistake and I'll change it...

2006-01-24 10:58:31

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 03/04] Add encryption ops to the keyctl syscall


David H?rdeman <[email protected]> wrote:

> Changes the keyctl syscall to accept six arguments (is it valid to do so?)
> and adds encryption as one of the supported ops for in-kernel keys.

I tried to avoid doing that for it required arch support as I recall, but I'm
not sure which arch I was thinking of. It looks like it ought to be okay...
it's no worse than mmap.

> + * - should return the amount of data that would be returned from the
> + * encryption even if the buffer is NULL
> + * - expects the output buffer to be able to hold the result
> + */

Can you use TAB chars here please.

> #define KEYCTL_ASSUME_AUTHORITY 16 /* assume request_key() authorisation */
> +#define KEYCTL_ENCRYPT 17 /* encrypt a chunk of data using key */

And here.

> + key = key_ref_to_ptr(key_ref);
> +
> + /* see if we can read it directly */
> + ret = key_permission(key_ref, KEY_READ);

You don't actually need to calculate key until after you've done all those
checks, so I'd move it further down the file. You can use the function to
release key references in the error handling or have a separate error handling
return path.

> + down_read(&key->sem);
> + ret = key->type->encrypt(key, data, dlen, result, rlen);
> + up_read(&key->sem);

Do we really want to restrict the key type implementor to using the r/w
semaphore. Would it be better to let the type decide whether it wants to use
the semaphore or let it use RCU if it so desires?

David

2006-01-24 11:08:46

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 04/04] Add dsa key type

David H?rdeman <[email protected]> wrote:

> Adds the dsa in-kernel key type.

Please add a header file in include/keys/ to provide access to the key type
definition and any special payload structures you use.

> +static char *
> +sign(const struct key_payload_dsa *skey,

Please be consistent about sticking prefixes on the names of your
functions. This ought to be called something like dsa_sign.

> + printk("dsa: crypto_digest_setkey failed with error %i\n", i);

Should involve KERN_ERR or something like.

David

2006-01-24 11:09:56

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 03/04] Add encryption ops to the keyctl syscall


David H?rdeman <[email protected]> wrote:

> and adds encryption as one of the supported ops for in-kernel keys.

Do not forget to update Documentation/keys.txt.

David

2006-01-25 19:15:05

by David Härdeman

[permalink] [raw]
Subject: Re: [PATCH 04/04] Add dsa key type

On Tue, Jan 24, 2006 at 11:08:45AM +0000, David Howells wrote:
>David H?rdeman <[email protected]> wrote:
>
>> Adds the dsa in-kernel key type.
>
>Please add a header file in include/keys/ to provide access to the key type
>definition and any special payload structures you use.

That was already added by one of the earlier patches as include/dsa.h
since its shared by crypto/dsa.c and security/keys/dsa_key.c, do you
prefer some other solution?

>> +static char *
>> +sign(const struct key_payload_dsa *skey,
>
>Please be consistent about sticking prefixes on the names of your
>functions. This ought to be called something like dsa_sign.

Ok

>> + printk("dsa: crypto_digest_setkey failed with error %i\n", i);
>
>Should involve KERN_ERR or something like.

Ok

Regards,
David

2006-01-25 20:41:35

by David Härdeman

[permalink] [raw]
Subject: Re: [PATCH 03/04] Add encryption ops to the keyctl syscall

On Tue, Jan 24, 2006 at 10:58:24AM +0000, David Howells wrote:
>> + key = key_ref_to_ptr(key_ref);
>> +
>> + /* see if we can read it directly */
>> + ret = key_permission(key_ref, KEY_READ);
>
>You don't actually need to calculate key until after you've done all those
>checks, so I'd move it further down the file. You can use the function to
>release key references in the error handling or have a separate error handling
>return path.

Do you mean that I should move the key_ref_to_ptr call to right after
the can_read_key label? If that is the case, shouldn't the same thing be
done for keyctl_read_key?

>> + down_read(&key->sem);
>> + ret = key->type->encrypt(key, data, dlen, result, rlen);
>> + up_read(&key->sem);
>
>Do we really want to restrict the key type implementor to using the r/w
>semaphore. Would it be better to let the type decide whether it wants to use
>the semaphore or let it use RCU if it so desires?

Ok, I'll move the semaphore into the dsa key type instead and change the
appropriate comments.

Re,
David

2006-01-26 09:42:08

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 04/04] Add dsa key type

David H?rdeman <[email protected]> wrote:

> That was already added by one of the earlier patches as include/dsa.h since
> its shared by crypto/dsa.c and security/keys/dsa_key.c, do you prefer some
> other solution?

That'll do then.

David

2006-01-26 09:43:58

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 03/04] Add encryption ops to the keyctl syscall

David H?rdeman <[email protected]> wrote:

> Do you mean that I should move the key_ref_to_ptr call to right after the
> can_read_key label? If that is the case, shouldn't the same thing be done for
> keyctl_read_key?

Quite possibly. I'll have a look at it when I get back from NZ. Actually, it's
possible that the compiler shifts it down anyway since it has no side effects.

David