New s390 machines have hardware support for the generation of pseudo-random
numbers. This patch implements a simple char driver that exports this numbers
to user-space. Other possible implementations would have been:
* using the new hwrandom number generator API
PRO: reuse of an existing interface
CON: this API is for "truly" random data, not for pseudo-random numbers
* merging the s390 PRNG with the random pool implementation
PRO: no new interface, random numbers can be read through /dev/urandom
CON: complex implementation, could only use traditional /dev/urandom algorithm
or hardware-accelerated implementation
I've chosen the char driver since it allows the user to decide which pseudo-random
numbers he wants to use. That means there is a new interface for the s390
PRNG, called /dev/prandom.
I would like to know if there are any objections, especially with the chosen device
name.
Jan
---
arch/s390/crypto/Makefile | 1
arch/s390/crypto/crypt_s390.h | 3
arch/s390/crypto/crypt_s390_query.c | 2
arch/s390/crypto/prng.c | 206 ++++++++++++++++++++++++++++++++++++
arch/s390/defconfig | 1
drivers/s390/Kconfig | 7 +
include/asm-s390/timex.h | 12 ++
7 files changed, 231 insertions(+), 1 deletion(-)
diff -urNp linux-2.5/arch/s390/crypto/Makefile linux-2.5_prng/arch/s390/crypto/Makefile
--- linux-2.5/arch/s390/crypto/Makefile 2005-12-06 16:11:12.000000000 +0100
+++ linux-2.5_prng/arch/s390/crypto/Makefile 2006-12-01 13:04:27.000000000 +0100
@@ -6,5 +6,6 @@ obj-$(CONFIG_CRYPTO_SHA1_S390) += sha1_s
obj-$(CONFIG_CRYPTO_SHA256_S390) += sha256_s390.o
obj-$(CONFIG_CRYPTO_DES_S390) += des_s390.o des_check_key.o
obj-$(CONFIG_CRYPTO_AES_S390) += aes_s390.o
+obj-$(CONFIG_S390_PRNG) += prng.o
obj-$(CONFIG_CRYPTO_TEST) += crypt_s390_query.o
diff -urNp linux-2.5/arch/s390/crypto/crypt_s390.h linux-2.5_prng/arch/s390/crypto/crypt_s390.h
--- linux-2.5/arch/s390/crypto/crypt_s390.h 2006-12-01 12:54:23.000000000 +0100
+++ linux-2.5_prng/arch/s390/crypto/crypt_s390.h 2006-12-01 13:05:26.000000000 +0100
@@ -68,6 +68,7 @@ enum crypt_s390_kmc_func {
KMC_AES_192_DECRYPT = CRYPT_S390_KMC | 0x13 | 0x80,
KMC_AES_256_ENCRYPT = CRYPT_S390_KMC | 0x14,
KMC_AES_256_DECRYPT = CRYPT_S390_KMC | 0x14 | 0x80,
+ KMC_PRNG = CRYPT_S390_KMC | 0x43,
};
/* function codes for KIMD (COMPUTE INTERMEDIATE MESSAGE DIGEST)
@@ -147,7 +148,7 @@ crypt_s390_km(long func, void* param, u8
* @param src: address of source memory area
* @param src_len: length of src operand in bytes
* @returns < zero for failure, 0 for the query func, number of processed bytes
- * for encryption/decryption funcs
+ * for encryption/decryption funcs
*/
static inline int
crypt_s390_kmc(long func, void* param, u8* dest, const u8* src, long src_len)
diff -urNp linux-2.5/arch/s390/crypto/crypt_s390_query.c linux-2.5_prng/arch/s390/crypto/crypt_s390_query.c
--- linux-2.5/arch/s390/crypto/crypt_s390_query.c 2006-06-19 14:01:10.000000000 +0200
+++ linux-2.5_prng/arch/s390/crypto/crypt_s390_query.c 2006-12-01 13:05:46.000000000 +0100
@@ -54,6 +54,8 @@ static void query_available_functions(vo
crypt_s390_func_available(KMC_AES_192_ENCRYPT));
printk(KERN_INFO "KMC_AES_256: %d\n",
crypt_s390_func_available(KMC_AES_256_ENCRYPT));
+ printk(KERN_INFO "KMC_PRNG: %d\n",
+ crypt_s390_func_available(KMC_PRNG));
/* query available KIMD functions */
printk(KERN_INFO "KIMD_QUERY: %d\n",
diff -urNp linux-2.5/arch/s390/crypto/prng.c linux-2.5_prng/arch/s390/crypto/prng.c
--- linux-2.5/arch/s390/crypto/prng.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.5_prng/arch/s390/crypto/prng.c 2006-12-01 13:06:50.000000000 +0100
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2006 IBM Corporation
+ * Author(s): Jan Glauber <[email protected]>
+ * Driver for the s390 pseudo random number generator
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/random.h>
+#include <asm/debug.h>
+#include <asm/uaccess.h>
+
+#include "crypt_s390.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jan Glauber <[email protected]>");
+MODULE_DESCRIPTION("s390 PRNG interface");
+
+static int prng_chunk_size = 32;
+module_param(prng_chunk_size, int, 0);
+MODULE_PARM_DESC(prng_chunk_size, "PRNG read chunk size in bytes");
+
+static int prng_entropy_limit = 4096;
+module_param(prng_entropy_limit, int, 0);
+MODULE_PARM_DESC(prng_entropy_limit, "PRNG add entropy after that much bytes were produced");
+
+/*
+ * Any one who considers arithmetical methods of producing random digits is,
+ * of course, in a state of sin. -- John von Neumann
+ */
+
+struct s390_prng_data {
+ unsigned long count; /* how many bytes were produced */
+ char *buf;
+};
+
+struct s390_prng_data *p;
+
+/* copied from libica, use a non-zero initial parameter block */
+unsigned char parm_block[32] = {
+0x0F,0x2B,0x8E,0x63,0x8C,0x8E,0xD2,0x52,0x64,0xB7,0xA0,0x7B,0x75,0x28,0xB8,0xF4,
+0x75,0x5F,0xD2,0xA6,0x8D,0x97,0x11,0xFF,0x49,0xD8,0x23,0xF3,0x7E,0x21,0xEC,0xA0,
+};
+
+static int prng_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static void prng_add_entropy(void)
+{
+ __u64 entropy[4];
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < 16; i++) {
+ entropy[0] = get_clock();
+ entropy[1] = get_clock();
+ entropy[2] = get_clock();
+ entropy[3] = get_clock();
+ ret = crypt_s390_kmc(KMC_PRNG, parm_block, (char *)entropy,
+ (char *)entropy, sizeof(entropy));
+ BUG_ON(ret < 0 || ret != sizeof(entropy));
+ memcpy(parm_block, entropy, sizeof(entropy));
+ }
+}
+
+static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes,
+ loff_t *ppos)
+{
+ unsigned long long clock[2];
+ int chunk, n, x;
+ int ret = 0;
+ int tmp;
+
+ /* nbytes can be arbitrary long, we spilt it into chunks */
+ while (nbytes) {
+ /* same as in extract_entropy_user in random.c */
+ if (need_resched()) {
+ if (signal_pending(current)) {
+ if (ret == 0)
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ /*
+ * we lose some random bytes if an attacker issues
+ * reads < 8 bytes, but we don't care
+ */
+ chunk = min_t(int, nbytes, prng_chunk_size);
+
+ /* PRNG only likes multiples of 8 bytes */
+ n = (chunk + 7) & -8;
+
+ if (p->count > prng_entropy_limit)
+ prng_add_entropy();
+
+ /*
+ * It shouldn't weaken the quality of the random numbers
+ * passing the full 16 bytes from STCKE to the generator.
+ */
+ for (x=0; x < n/16; x+=2) {
+ get_clock_extended(&clock);
+ *(__u64 *)(p->buf + x*8) = clock[0];
+ *(__u64 *)(p->buf + x*8 + 88) = clock[1];
+ }
+ if (n % 16) {
+ get_clock_extended(&clock);
+ *(__u64 *)(p->buf + x*8) = clock[0];
+ }
+
+ tmp = crypt_s390_kmc(KMC_PRNG, parm_block, p->buf, p->buf, n);
+ BUG_ON((tmp < 0) || (tmp != n));
+
+ p->count += n;
+
+ if (copy_to_user(ubuf, p->buf, chunk))
+ return -EFAULT;
+
+ nbytes -= chunk;
+ ret += chunk;
+ ubuf += chunk;
+ }
+ return ret;
+}
+
+static struct file_operations prng_fops = {
+ .owner = THIS_MODULE,
+ .open = &prng_open,
+ .release = NULL,
+ .read = &prng_read,
+};
+
+static struct miscdevice prng_dev = {
+ .name = "prandom",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &prng_fops,
+};
+
+static int __init prng_init(void)
+{
+ int nbytes = 16;
+ char buf[nbytes];
+ int i = 0;
+ int ret;
+
+ /* check if the CPU has a PRNG */
+ if (!crypt_s390_func_available(KMC_PRNG))
+ return -ENOTSUPP;
+
+ /* initialize the PRNG, add 128 bits of entropy */
+ get_random_bytes(buf, 16);
+ while (nbytes >= 8) {
+ *((__u64 *)parm_block) ^= *((__u64 *)buf+i*8);
+ prng_add_entropy();
+ i += 8;
+ nbytes -= 8;
+ }
+ prng_add_entropy();
+
+ ret = misc_register(&prng_dev);
+ if (ret) {
+ printk(KERN_WARNING
+ "s390 PRNG driver not loaded. Could not register misc device.\n");
+ goto out;
+ }
+
+ p = kmalloc(sizeof(struct s390_prng_data), GFP_KERNEL);
+ if (!p) {
+ ret = -ENOMEM;
+ goto out_dev;
+ }
+ p->count = 0;
+
+ p->buf = kmalloc(prng_chunk_size, GFP_KERNEL);
+ if (!p->buf) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ return 0;
+
+out_free:
+ kfree(p);
+out_dev:
+ misc_deregister(&prng_dev);
+out:
+ return ret;
+}
+
+static void __exit prng_exit(void)
+{
+ /* wipe me */
+ memset(p->buf, 0, prng_chunk_size);
+ kfree(p->buf);
+ kfree(p);
+
+ misc_deregister(&prng_dev);
+}
+
+module_init(prng_init);
+module_exit(prng_exit);
diff -urNp linux-2.5/arch/s390/defconfig linux-2.5_prng/arch/s390/defconfig
--- linux-2.5/arch/s390/defconfig 2006-12-01 12:54:23.000000000 +0100
+++ linux-2.5_prng/arch/s390/defconfig 2006-12-01 13:04:27.000000000 +0100
@@ -437,6 +437,7 @@ CONFIG_MONWRITER=m
#
CONFIG_ZCRYPT=m
# CONFIG_ZCRYPT_MONOLITHIC is not set
+CONFIG_S390_PRNG=m
#
# Network device support
diff -urNp linux-2.5/drivers/s390/Kconfig linux-2.5_prng/drivers/s390/Kconfig
--- linux-2.5/drivers/s390/Kconfig 2006-08-21 13:54:41.000000000 +0200
+++ linux-2.5_prng/drivers/s390/Kconfig 2006-12-01 13:04:27.000000000 +0100
@@ -244,4 +244,11 @@ config ZCRYPT_MONOLITHIC
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
+config S390_PRNG
+ tristate "Support for pseudo random number generator device driver"
+ help
+ Select this option if you want to use the s390 pseudo random number generator.
+ The PRNG is part of the cryptograhic processor functions and produces
+ ANSI X9.17 conform numbers. The PRNG is usable via the char device /dev/prandom.
+
endmenu
diff -urNp linux-2.5/include/asm-s390/timex.h linux-2.5_prng/include/asm-s390/timex.h
--- linux-2.5/include/asm-s390/timex.h 2006-11-28 12:40:12.000000000 +0100
+++ linux-2.5_prng/include/asm-s390/timex.h 2006-12-01 13:04:27.000000000 +0100
@@ -62,6 +62,18 @@ static inline unsigned long long get_clo
return clk;
}
+static inline void get_clock_extended(void *dest)
+{
+ typedef struct { unsigned long long clk[2]; } clock_t;
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
+ asm volatile("stcke %0" : "=Q" (*((clock_t *)dest)) : : "cc");
+#else /* __GNUC__ */
+ asm volatile("stcke 0(%1)" : "=m" (*((clock_t *)dest))
+ : "a" ((clock_t *)dest) : "cc");
+#endif /* __GNUC__ */
+}
+
static inline cycles_t get_cycles(void)
{
return (cycles_t) get_clock() >> 2;
> * merging the s390 PRNG with the random pool implementation
> PRO: no new interface, random numbers can be read through /dev/urandom
> CON: complex implementation, could only use traditional /dev/urandom algorithm
> or hardware-accelerated implementation
Also PRO: Can be verified by non-IBM people, high resistance if there is
a flaw (accidental or US government 8)) in the 390 hardware.
> I've chosen the char driver since it allows the user to decide which pseudo-random
> numbers he wants to use. That means there is a new interface for the s390
> PRNG, called /dev/prandom.
And people can pipe this into the urandom layer if they wish.
> +/* copied from libica, use a non-zero initial parameter block */
> +unsigned char parm_block[32] = {
> +0x0F,0x2B,0x8E,0x63,0x8C,0x8E,0xD2,0x52,0x64,0xB7,0xA0,0x7B,0x75,0x28,0xB8,0xF4,
> +0x75,0x5F,0xD2,0xA6,0x8D,0x97,0x11,0xFF,0x49,0xD8,0x23,0xF3,0x7E,0x21,0xEC,0xA0,
> +};
> +
Global variables called "p" and "parm_block". Plus parm_block appears to
be const
Also your register the misc device before allocating the buffer so it can
be opened in theory before the alloc is done and crash.
On Fri, 2006-12-01 at 13:39 +0000, Alan wrote:
> > * merging the s390 PRNG with the random pool implementation
> > PRO: no new interface, random numbers can be read through /dev/urandom
> > CON: complex implementation, could only use traditional /dev/urandom algorithm
> > or hardware-accelerated implementation
>
> Also PRO: Can be verified by non-IBM people, high resistance if there is
> a flaw (accidental or US government 8)) in the 390 hardware.
No, the interesting stuff is done internally in the hardware, we cannot
see it with both implementations.
> > I've chosen the char driver since it allows the user to decide which pseudo-random
> > numbers he wants to use. That means there is a new interface for the s390
> > PRNG, called /dev/prandom.
>
> And people can pipe this into the urandom layer if they wish.
Yes, a user can just symlink urandom to prandom and will have a faster
generator.
> > +/* copied from libica, use a non-zero initial parameter block */
> > +unsigned char parm_block[32] = {
> > +0x0F,0x2B,0x8E,0x63,0x8C,0x8E,0xD2,0x52,0x64,0xB7,0xA0,0x7B,0x75,0x28,0xB8,0xF4,
> > +0x75,0x5F,0xD2,0xA6,0x8D,0x97,0x11,0xFF,0x49,0xD8,0x23,0xF3,0x7E,0x21,0xEC,0xA0,
> > +};
> > +
>
> Global variables called "p" and "parm_block". Plus parm_block appears to
> be const
Argh. I'll make them static.
> Also your register the misc device before allocating the buffer so it can
> be opened in theory before the alloc is done and crash.
Fixed. The misc_register is now done at the very end.
thanks,
Jan
---
arch/s390/crypto/Makefile | 1
arch/s390/crypto/crypt_s390.h | 3
arch/s390/crypto/crypt_s390_query.c | 2
arch/s390/crypto/prng.c | 203 ++++++++++++++++++++++++++++++++++++
arch/s390/defconfig | 1
drivers/s390/Kconfig | 7 +
include/asm-s390/timex.h | 12 ++
7 files changed, 228 insertions(+), 1 deletion(-)
diff -urNp linux-2.5/arch/s390/crypto/Makefile linux-2.5_prng/arch/s390/crypto/Makefile
--- linux-2.5/arch/s390/crypto/Makefile 2006-12-01 15:40:54.000000000 +0100
+++ linux-2.5_prng/arch/s390/crypto/Makefile 2006-11-30 18:30:23.000000000 +0100
@@ -6,5 +6,6 @@ obj-$(CONFIG_CRYPTO_SHA1_S390) += sha1_s
obj-$(CONFIG_CRYPTO_SHA256_S390) += sha256_s390.o
obj-$(CONFIG_CRYPTO_DES_S390) += des_s390.o des_check_key.o
obj-$(CONFIG_CRYPTO_AES_S390) += aes_s390.o
+obj-$(CONFIG_S390_PRNG) += prng.o
obj-$(CONFIG_CRYPTO_TEST) += crypt_s390_query.o
diff -urNp linux-2.5/arch/s390/crypto/crypt_s390.h linux-2.5_prng/arch/s390/crypto/crypt_s390.h
--- linux-2.5/arch/s390/crypto/crypt_s390.h 2006-12-01 15:40:54.000000000 +0100
+++ linux-2.5_prng/arch/s390/crypto/crypt_s390.h 2006-12-01 15:38:00.000000000 +0100
@@ -68,6 +68,7 @@ enum crypt_s390_kmc_func {
KMC_AES_192_DECRYPT = CRYPT_S390_KMC | 0x13 | 0x80,
KMC_AES_256_ENCRYPT = CRYPT_S390_KMC | 0x14,
KMC_AES_256_DECRYPT = CRYPT_S390_KMC | 0x14 | 0x80,
+ KMC_PRNG = CRYPT_S390_KMC | 0x43,
};
/* function codes for KIMD (COMPUTE INTERMEDIATE MESSAGE DIGEST)
@@ -147,7 +148,7 @@ crypt_s390_km(long func, void* param, u8
* @param src: address of source memory area
* @param src_len: length of src operand in bytes
* @returns < zero for failure, 0 for the query func, number of processed bytes
- * for encryption/decryption funcs
+ * for encryption/decryption funcs
*/
static inline int
crypt_s390_kmc(long func, void* param, u8* dest, const u8* src, long src_len)
diff -urNp linux-2.5/arch/s390/crypto/crypt_s390_query.c linux-2.5_prng/arch/s390/crypto/crypt_s390_query.c
--- linux-2.5/arch/s390/crypto/crypt_s390_query.c 2006-12-01 15:40:54.000000000 +0100
+++ linux-2.5_prng/arch/s390/crypto/crypt_s390_query.c 2006-12-01 15:37:27.000000000 +0100
@@ -54,6 +54,8 @@ static void query_available_functions(vo
crypt_s390_func_available(KMC_AES_192_ENCRYPT));
printk(KERN_INFO "KMC_AES_256: %d\n",
crypt_s390_func_available(KMC_AES_256_ENCRYPT));
+ printk(KERN_INFO "KMC_PRNG: %d\n",
+ crypt_s390_func_available(KMC_PRNG));
/* query available KIMD functions */
printk(KERN_INFO "KIMD_QUERY: %d\n",
diff -urNp linux-2.5/arch/s390/crypto/prng.c linux-2.5_prng/arch/s390/crypto/prng.c
--- linux-2.5/arch/s390/crypto/prng.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.5_prng/arch/s390/crypto/prng.c 2006-12-01 16:14:22.000000000 +0100
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2006 IBM Corporation
+ * Author(s): Jan Glauber <[email protected]>
+ * Driver for the s390 pseudo random number generator
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/random.h>
+#include <asm/debug.h>
+#include <asm/uaccess.h>
+
+#include "crypt_s390.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jan Glauber <[email protected]>");
+MODULE_DESCRIPTION("s390 PRNG interface");
+
+static int prng_chunk_size = 32;
+module_param(prng_chunk_size, int, 0);
+MODULE_PARM_DESC(prng_chunk_size, "PRNG read chunk size in bytes");
+
+static int prng_entropy_limit = 4096;
+module_param(prng_entropy_limit, int, 0);
+MODULE_PARM_DESC(prng_entropy_limit, "PRNG add entropy after that much bytes were produced");
+
+/*
+ * Any one who considers arithmetical methods of producing random digits is,
+ * of course, in a state of sin. -- John von Neumann
+ */
+
+struct s390_prng_data {
+ unsigned long count; /* how many bytes were produced */
+ char *buf;
+};
+
+static struct s390_prng_data *p;
+
+/* copied from libica, use a non-zero initial parameter block */
+static unsigned char parm_block[32] = {
+0x0F,0x2B,0x8E,0x63,0x8C,0x8E,0xD2,0x52,0x64,0xB7,0xA0,0x7B,0x75,0x28,0xB8,0xF4,
+0x75,0x5F,0xD2,0xA6,0x8D,0x97,0x11,0xFF,0x49,0xD8,0x23,0xF3,0x7E,0x21,0xEC,0xA0,
+};
+
+static int prng_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static void prng_add_entropy(void)
+{
+ __u64 entropy[4];
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < 16; i++) {
+ entropy[0] = get_clock();
+ entropy[1] = get_clock();
+ entropy[2] = get_clock();
+ entropy[3] = get_clock();
+ ret = crypt_s390_kmc(KMC_PRNG, parm_block, (char *)entropy,
+ (char *)entropy, sizeof(entropy));
+ BUG_ON(ret < 0 || ret != sizeof(entropy));
+ memcpy(parm_block, entropy, sizeof(entropy));
+ }
+}
+
+static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes,
+ loff_t *ppos)
+{
+ unsigned long long clock[2];
+ int chunk, n, x;
+ int ret = 0;
+ int tmp;
+
+ /* nbytes can be arbitrary long, we spilt it into chunks */
+ while (nbytes) {
+ /* same as in extract_entropy_user in random.c */
+ if (need_resched()) {
+ if (signal_pending(current)) {
+ if (ret == 0)
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ /*
+ * we lose some random bytes if an attacker issues
+ * reads < 8 bytes, but we don't care
+ */
+ chunk = min_t(int, nbytes, prng_chunk_size);
+
+ /* PRNG only likes multiples of 8 bytes */
+ n = (chunk + 7) & -8;
+
+ if (p->count > prng_entropy_limit)
+ prng_add_entropy();
+
+ /*
+ * It shouldn't weaken the quality of the random numbers
+ * passing the full 16 bytes from STCKE to the generator.
+ */
+ for (x=0; x < n/16; x+=2) {
+ get_clock_extended(&clock);
+ *(__u64 *)(p->buf + x*8) = clock[0];
+ *(__u64 *)(p->buf + x*8 + 88) = clock[1];
+ }
+ if (n % 16) {
+ get_clock_extended(&clock);
+ *(__u64 *)(p->buf + x*8) = clock[0];
+ }
+
+ tmp = crypt_s390_kmc(KMC_PRNG, parm_block, p->buf, p->buf, n);
+ BUG_ON((tmp < 0) || (tmp != n));
+
+ p->count += n;
+
+ if (copy_to_user(ubuf, p->buf, chunk))
+ return -EFAULT;
+
+ nbytes -= chunk;
+ ret += chunk;
+ ubuf += chunk;
+ }
+ return ret;
+}
+
+static struct file_operations prng_fops = {
+ .owner = THIS_MODULE,
+ .open = &prng_open,
+ .release = NULL,
+ .read = &prng_read,
+};
+
+static struct miscdevice prng_dev = {
+ .name = "prandom",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &prng_fops,
+};
+
+static int __init prng_init(void)
+{
+ int nbytes = 16;
+ char buf[nbytes];
+ int i = 0;
+ int ret;
+
+ /* check if the CPU has a PRNG */
+ if (!crypt_s390_func_available(KMC_PRNG))
+ return -ENOTSUPP;
+
+ p = kmalloc(sizeof(struct s390_prng_data), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->count = 0;
+
+ p->buf = kmalloc(prng_chunk_size, GFP_KERNEL);
+ if (!p->buf) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ /* initialize the PRNG, add 128 bits of entropy */
+ get_random_bytes(buf, 16);
+ while (nbytes >= 8) {
+ *((__u64 *)parm_block) ^= *((__u64 *)buf+i*8);
+ prng_add_entropy();
+ i += 8;
+ nbytes -= 8;
+ }
+ prng_add_entropy();
+
+ ret = misc_register(&prng_dev);
+ if (ret) {
+ printk(KERN_WARNING
+ "s390 PRNG driver not loaded. Could not register misc device.\n");
+ goto out_buf;
+ }
+ return 0;
+
+out_buf:
+ kfree(p->buf);
+out_free:
+ kfree(p);
+ return ret;
+}
+
+static void __exit prng_exit(void)
+{
+ /* wipe me */
+ memset(p->buf, 0, prng_chunk_size);
+ kfree(p->buf);
+ kfree(p);
+
+ misc_deregister(&prng_dev);
+}
+
+module_init(prng_init);
+module_exit(prng_exit);
diff -urNp linux-2.5/arch/s390/defconfig linux-2.5_prng/arch/s390/defconfig
--- linux-2.5/arch/s390/defconfig 2006-12-01 15:40:55.000000000 +0100
+++ linux-2.5_prng/arch/s390/defconfig 2006-11-30 18:30:23.000000000 +0100
@@ -437,6 +437,7 @@ CONFIG_MONWRITER=m
#
CONFIG_ZCRYPT=m
# CONFIG_ZCRYPT_MONOLITHIC is not set
+CONFIG_S390_PRNG=m
#
# Network device support
diff -urNp linux-2.5/drivers/s390/Kconfig linux-2.5_prng/drivers/s390/Kconfig
--- linux-2.5/drivers/s390/Kconfig 2006-12-01 15:40:55.000000000 +0100
+++ linux-2.5_prng/drivers/s390/Kconfig 2006-11-30 18:30:23.000000000 +0100
@@ -244,4 +244,11 @@ config ZCRYPT_MONOLITHIC
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
+config S390_PRNG
+ tristate "Support for pseudo random number generator device driver"
+ help
+ Select this option if you want to use the s390 pseudo random number generator.
+ The PRNG is part of the cryptograhic processor functions and produces
+ ANSI X9.17 conform numbers. The PRNG is usable via the char device /dev/prandom.
+
endmenu
diff -urNp linux-2.5/include/asm-s390/timex.h linux-2.5_prng/include/asm-s390/timex.h
--- linux-2.5/include/asm-s390/timex.h 2006-12-01 15:40:55.000000000 +0100
+++ linux-2.5_prng/include/asm-s390/timex.h 2006-11-30 18:30:23.000000000 +0100
@@ -62,6 +62,18 @@ static inline unsigned long long get_clo
return clk;
}
+static inline void get_clock_extended(void *dest)
+{
+ typedef struct { unsigned long long clk[2]; } clock_t;
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
+ asm volatile("stcke %0" : "=Q" (*((clock_t *)dest)) : : "cc");
+#else /* __GNUC__ */
+ asm volatile("stcke 0(%1)" : "=m" (*((clock_t *)dest))
+ : "a" ((clock_t *)dest) : "cc");
+#endif /* __GNUC__ */
+}
+
static inline cycles_t get_cycles(void)
{
return (cycles_t) get_clock() >> 2;
On Fri, 01 Dec 2006 16:20:46 +0100
Jan Glauber <[email protected]> wrote:
> Yes, a user can just symlink urandom to prandom and will have a faster
> generator.
More usefully they can use it as an entropy source with an entropy
daemon to feed it into the standard urandom/random.
Alan <[email protected]> wrote:
> On Fri, 01 Dec 2006 16:20:46 +0100
> Jan Glauber <[email protected]> wrote:
>> Yes, a user can just symlink urandom to prandom and will have a faster
>> generator.
>
>
> More usefully they can use it as an entropy source with an entropy
> daemon to feed it into the standard urandom/random.
Only if other users will randomly drain /dev/prandom, otherwise you might
as well use /dev/zero.
--
Ich danke GMX daf?r, die Verwendung meiner Adressen mittels per SPF
verbreiteten L?gen zu sabotieren.
http://david.woodhou.se/why-not-spf.html
On Fri, 01 Dec 2006 14:19:15 +0100, Jan Glauber said:
> New s390 machines have hardware support for the generation of pseudo-random
> numbers. This patch implements a simple char driver that exports this numbers
> to user-space. Other possible implementations would have been:
> + for (i = 0; i < 16; i++) {
> + entropy[0] = get_clock();
> + entropy[1] = get_clock();
> + entropy[2] = get_clock();
> + entropy[3] = get_clock();
By the time this loop completes, we'll have done 64 get_clock() - and if an
attacker has a good estimate of what the system clock has in it, they'll be
able to guess all 64 values, since each pass through the loop will have fairly
predictable timing. So as a result, the pseudo-random stream will be a *lot*
less random than one would hope for...
> + /*
> + * It shouldn't weaken the quality of the random numbers
> + * passing the full 16 bytes from STCKE to the generator.
> + */
As long as you realize that probably 12 or 13 or even more of those 16 bytes
are likely predictable (depending exactly how fast the hardware clock ticks),
and as a result the output stream will also be predictable.
I think this needs to either find a way to stir in entropy from sources other
than the clock, or make it clear that the returned data is pseudo-random but
likely predictable by a determined attacker. As such, it's probably a bad
choice for many things that /dev/urandom is usable for, such as session keys
and the like.
On Mon, 2006-12-04 at 11:15 -0500, [email protected] wrote:
> On Fri, 01 Dec 2006 14:19:15 +0100, Jan Glauber said:
> > New s390 machines have hardware support for the generation of pseudo-random
> > numbers. This patch implements a simple char driver that exports this numbers
> > to user-space. Other possible implementations would have been:
>
> > + for (i = 0; i < 16; i++) {
> > + entropy[0] = get_clock();
> > + entropy[1] = get_clock();
> > + entropy[2] = get_clock();
> > + entropy[3] = get_clock();
>
> By the time this loop completes, we'll have done 64 get_clock() - and if an
> attacker has a good estimate of what the system clock has in it, they'll be
> able to guess all 64 values, since each pass through the loop will have fairly
> predictable timing. So as a result, the pseudo-random stream will be a *lot*
> less random than one would hope for...
I completely agree. Filling the input buffer with timestamps looks quite
uncomfortable but was exactly what the hardware specification suggested.
At least for the initialisation of the PRNG I preferred get_random_bytes()
over get_clock to get a good initial seed. But get_random_bytes cannot
be used during normal operation since the PRNG read should not block.
> > + /*
> > + * It shouldn't weaken the quality of the random numbers
> > + * passing the full 16 bytes from STCKE to the generator.
> > + */
>
> As long as you realize that probably 12 or 13 or even more of those 16 bytes
> are likely predictable (depending exactly how fast the hardware clock ticks),
> and as a result the output stream will also be predictable.
Yes, if an attacker knows the initial clock value a brute-force attack
would be feasible to predict the output. But I don't know if the
hardware completely relies on the clock values or if there is any
internal state which is not visible by an attacker. I will try to find
out more details...
Jan
On Friday 01 December 2006 14:19, Jan Glauber wrote:
> I've chosen the char driver since it allows the user to decide which pseudo-random
> numbers he wants to use. That means there is a new interface for the s390
> PRNG, called /dev/prandom.
>
> I would like to know if there are any objections, especially with the chosen device
> name.
This may be a stupid question, but what is it _good_ for? My understanding is
that the crypt_s390_kmc() opcodes work in user mode as well as kernel mode, so
you should not need a character device at all, but maybe just a small tool
that spits prandom data to stdout.
Arnd <><
On Thu, 2006-12-07 at 16:06 +0100, Arnd Bergmann wrote:
> On Friday 01 December 2006 14:19, Jan Glauber wrote:
> > I've chosen the char driver since it allows the user to decide which pseudo-random
> > numbers he wants to use. That means there is a new interface for the s390
> > PRNG, called /dev/prandom.
> >
> > I would like to know if there are any objections, especially with the chosen device
> > name.
>
> This may be a stupid question, but what is it _good_ for? My understanding is
> that the crypt_s390_kmc() opcodes work in user mode as well as kernel mode, so
> you should not need a character device at all, but maybe just a small tool
> that spits prandom data to stdout.
Hm, why is /dev/urandom implemented in the kernel?
It could be done completely in user-space (like libica already does)
but I think having a device node where you can read from is the simplest
implementation. Also, if we can solve the security flaw we could use it
as replacement for /dev/urandom.
Jan
On Thursday 07 December 2006 16:19, Jan Glauber wrote:
> Hm, why is /dev/urandom implemented in the kernel?
>
> It could be done completely in user-space (like libica already does)
> but I think having a device node where you can read from is the simplest
> implementation. Also, if we can solve the security flaw we could use it
> as replacement for /dev/urandom.
urandom is more useful, because can't be implemented in user space at
all. /dev/urandom will use the real randomness from the kernel as a seed
without depleting the entropy pool. How does your /dev/prandom device
compare to /dev/urandom performance-wise? If it can be made to use
the same input data and it turns out to be significantly faster, I can
see some use for it.
Arnd <><
On Thu, 2006-12-07 at 19:43 +0100, Arnd Bergmann wrote:
> On Thursday 07 December 2006 16:19, Jan Glauber wrote:
> > Hm, why is /dev/urandom implemented in the kernel?
> >
> > It could be done completely in user-space (like libica already does)
> > but I think having a device node where you can read from is the simplest
> > implementation. Also, if we can solve the security flaw we could use it
> > as replacement for /dev/urandom.
>
> urandom is more useful, because can't be implemented in user space at
> all. /dev/urandom will use the real randomness from the kernel as a seed
> without depleting the entropy pool. How does your /dev/prandom device
> compare to /dev/urandom performance-wise? If it can be made to use
> the same input data and it turns out to be significantly faster, I can
> see some use for it.
The performance of the PRNG without constantly adding entropy is up tp
factor 40 faster than /dev/urandom ;- , depending on the block size of
the read.
With the current patch it performs not so well because of the STCKE loop
before every KMC. I think about removing them and changing the
periodically seed to use get_random_bytes instead.
Jan
On Tue, 2006-12-05 at 14:07 +0100, Jan Glauber wrote:
> Yes, if an attacker knows the initial clock value a brute-force attack
> would be feasible to predict the output. But I don't know if the
> hardware completely relies on the clock values or if there is any
> internal state which is not visible by an attacker. I will try to find
> out more details...
OK, after some research I got a new proposal. What the hardware does
internally is just a Triple-DES operation with 3 different keys.
That means with my old implementation the input for a TDES operation is
the buffer full of timestamps. If an attacker can guess the first timestamp
he knows the entire input, since the timestamp loop indeed produces
timestamps with a constant offset. Therefore I'd like to replace the
timestamp loop with only a single timestamp, which has the following
advantages:
- it is as safe as the loop (since the additional timestamps contain very
low entropy)
- its faster, since we don't have to overwrite the entire buffer
- the one timestamp still guarantees unique, non-repeating output values
Even if an attacker knows the input buffer for the next operation
(known-plaintext attack) it turns out that currently this attack is not
feasible against TDES. Therefore, as long as the key is not compromised
there is no way to guess the next output from the generator.
Jan
--
arch/s390/crypto/Makefile | 1
arch/s390/crypto/crypt_s390.h | 3
arch/s390/crypto/crypt_s390_query.c | 2
arch/s390/crypto/prng.c | 209 ++++++++++++++++++++++++++++++++++++
arch/s390/defconfig | 1
drivers/s390/Kconfig | 7 +
6 files changed, 222 insertions(+), 1 deletion(-)
diff -urNp linux-2.6.orig/arch/s390/crypto/Makefile linux-2.6/arch/s390/crypto/Makefile
--- linux-2.6.orig/arch/s390/crypto/Makefile 2006-11-17 10:26:35.000000000 +0100
+++ linux-2.6/arch/s390/crypto/Makefile 2007-01-16 13:03:12.000000000 +0100
@@ -6,5 +6,6 @@ obj-$(CONFIG_CRYPTO_SHA1_S390) += sha1_s
obj-$(CONFIG_CRYPTO_SHA256_S390) += sha256_s390.o
obj-$(CONFIG_CRYPTO_DES_S390) += des_s390.o des_check_key.o
obj-$(CONFIG_CRYPTO_AES_S390) += aes_s390.o
+obj-$(CONFIG_S390_PRNG) += prng.o
obj-$(CONFIG_CRYPTO_TEST) += crypt_s390_query.o
diff -urNp linux-2.6.orig/arch/s390/crypto/crypt_s390.h linux-2.6/arch/s390/crypto/crypt_s390.h
--- linux-2.6.orig/arch/s390/crypto/crypt_s390.h 2006-11-17 10:26:35.000000000 +0100
+++ linux-2.6/arch/s390/crypto/crypt_s390.h 2007-01-16 13:03:12.000000000 +0100
@@ -68,6 +68,7 @@ enum crypt_s390_kmc_func {
KMC_AES_192_DECRYPT = CRYPT_S390_KMC | 0x13 | 0x80,
KMC_AES_256_ENCRYPT = CRYPT_S390_KMC | 0x14,
KMC_AES_256_DECRYPT = CRYPT_S390_KMC | 0x14 | 0x80,
+ KMC_PRNG = CRYPT_S390_KMC | 0x43,
};
/* function codes for KIMD (COMPUTE INTERMEDIATE MESSAGE DIGEST)
@@ -147,7 +148,7 @@ crypt_s390_km(long func, void* param, u8
* @param src: address of source memory area
* @param src_len: length of src operand in bytes
* @returns < zero for failure, 0 for the query func, number of processed bytes
- * for encryption/decryption funcs
+ * for encryption/decryption funcs
*/
static inline int
crypt_s390_kmc(long func, void* param, u8* dest, const u8* src, long src_len)
diff -urNp linux-2.6.orig/arch/s390/crypto/crypt_s390_query.c linux-2.6/arch/s390/crypto/crypt_s390_query.c
--- linux-2.6.orig/arch/s390/crypto/crypt_s390_query.c 2006-11-17 10:26:35.000000000 +0100
+++ linux-2.6/arch/s390/crypto/crypt_s390_query.c 2007-01-16 13:03:12.000000000 +0100
@@ -54,6 +54,8 @@ static void query_available_functions(vo
crypt_s390_func_available(KMC_AES_192_ENCRYPT));
printk(KERN_INFO "KMC_AES_256: %d\n",
crypt_s390_func_available(KMC_AES_256_ENCRYPT));
+ printk(KERN_INFO "KMC_PRNG: %d\n",
+ crypt_s390_func_available(KMC_PRNG));
/* query available KIMD functions */
printk(KERN_INFO "KIMD_QUERY: %d\n",
diff -urNp linux-2.6.orig/arch/s390/crypto/prng.c linux-2.6/arch/s390/crypto/prng.c
--- linux-2.6.orig/arch/s390/crypto/prng.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6/arch/s390/crypto/prng.c 2007-01-16 13:04:45.000000000 +0100
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2006 IBM Corporation
+ * Author(s): Jan Glauber <[email protected]>
+ * Driver for the s390 pseudo random number generator
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/random.h>
+#include <asm/debug.h>
+#include <asm/uaccess.h>
+
+#include "crypt_s390.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jan Glauber <[email protected]>");
+MODULE_DESCRIPTION("s390 PRNG interface");
+
+static int prng_chunk_size = 256;
+module_param(prng_chunk_size, int, 0);
+MODULE_PARM_DESC(prng_chunk_size, "PRNG read chunk size in bytes");
+
+static int prng_entropy_limit = 4096;
+module_param(prng_entropy_limit, int, 0);
+MODULE_PARM_DESC(prng_entropy_limit,
+ "PRNG add entropy after that much bytes were produced");
+
+/*
+ * Any one who considers arithmetical methods of producing random digits is,
+ * of course, in a state of sin. -- John von Neumann
+ */
+
+struct s390_prng_data {
+ unsigned long count; /* how many bytes were produced */
+ char *buf;
+};
+
+static struct s390_prng_data *p;
+
+/* copied from libica, use a non-zero initial parameter block */
+static unsigned char parm_block[32] = {
+0x0F,0x2B,0x8E,0x63,0x8C,0x8E,0xD2,0x52,0x64,0xB7,0xA0,0x7B,0x75,0x28,0xB8,0xF4,
+0x75,0x5F,0xD2,0xA6,0x8D,0x97,0x11,0xFF,0x49,0xD8,0x23,0xF3,0x7E,0x21,0xEC,0xA0,
+};
+
+static int prng_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static void prng_add_entropy(void)
+{
+ __u64 entropy[4];
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < 16; i++) {
+ ret = crypt_s390_kmc(KMC_PRNG, parm_block, (char *)entropy,
+ (char *)entropy, sizeof(entropy));
+ BUG_ON(ret < 0 || ret != sizeof(entropy));
+ memcpy(parm_block, entropy, sizeof(entropy));
+ }
+}
+
+static void prng_seed(int nbytes)
+{
+ char buf[nbytes];
+ int i = 0;
+
+ get_random_bytes(buf, nbytes);
+
+ /* Add the entropy */
+ while (nbytes >= 8) {
+ *((__u64 *)parm_block) ^= *((__u64 *)buf+i*8);
+ prng_add_entropy();
+ i += 8;
+ nbytes -= 8;
+ }
+ prng_add_entropy();
+}
+
+static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes,
+ loff_t *ppos)
+{
+ int chunk, n;
+ int ret = 0;
+ int tmp;
+
+ /* nbytes can be arbitrary long, we spilt it into chunks */
+ while (nbytes) {
+ /* same as in extract_entropy_user in random.c */
+ if (need_resched()) {
+ if (signal_pending(current)) {
+ if (ret == 0)
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ /*
+ * we lose some random bytes if an attacker issues
+ * reads < 8 bytes, but we don't care
+ */
+ chunk = min_t(int, nbytes, prng_chunk_size);
+
+ /* PRNG only likes multiples of 8 bytes */
+ n = (chunk + 7) & -8;
+
+ if (p->count > prng_entropy_limit)
+ prng_seed(8);
+
+ /* if the CPU supports PRNG stckf is present too */
+ asm volatile(".insn s,0xb27c0000,%0"
+ : "=m" (*((unsigned long long *)p->buf)) : : "cc");
+
+ /*
+ * Beside the STCKF the input for the TDES-EDE is the output
+ * of the last operation. We differ here from X9.17 since we
+ * only store one timestamp into the buffer. Padding the whole
+ * buffer with timestamps does not improve security, since
+ * successive stckf have nearly constant offsets.
+ * If an attacker knows the first timestamp it would be
+ * trivial to guess the additional values. One timestamp
+ * is therefore enough and still guarantees unique input values.
+ *
+ * Note: you can still get strict X9.17 conformity by setting
+ * prng_chunk_size to 8 bytes.
+ */
+ tmp = crypt_s390_kmc(KMC_PRNG, parm_block, p->buf, p->buf, n);
+ BUG_ON((tmp < 0) || (tmp != n));
+
+ p->count += n;
+
+ if (copy_to_user(ubuf, p->buf, chunk))
+ return -EFAULT;
+
+ nbytes -= chunk;
+ ret += chunk;
+ ubuf += chunk;
+ }
+ return ret;
+}
+
+static struct file_operations prng_fops = {
+ .owner = THIS_MODULE,
+ .open = &prng_open,
+ .release = NULL,
+ .read = &prng_read,
+};
+
+static struct miscdevice prng_dev = {
+ .name = "prandom",
+ .minor = MISC_DYNAMIC_MINOR,
+ .fops = &prng_fops,
+};
+
+static int __init prng_init(void)
+{
+ int ret;
+
+ /* check if the CPU has a PRNG */
+ if (!crypt_s390_func_available(KMC_PRNG))
+ return -ENOTSUPP;
+
+ p = kmalloc(sizeof(struct s390_prng_data), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->count = 0;
+
+ p->buf = kmalloc(prng_chunk_size, GFP_KERNEL);
+ if (!p->buf) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ /* initialize the PRNG, add 128 bits of entropy */
+ prng_seed(16);
+
+ ret = misc_register(&prng_dev);
+ if (ret) {
+ printk(KERN_WARNING
+ "s390 PRNG driver not loaded. Could not register misc device.\n");
+ goto out_buf;
+ }
+ return 0;
+
+out_buf:
+ kfree(p->buf);
+out_free:
+ kfree(p);
+ return ret;
+}
+
+static void __exit prng_exit(void)
+{
+ /* wipe me */
+ memset(p->buf, 0, prng_chunk_size);
+ kfree(p->buf);
+ kfree(p);
+
+ misc_deregister(&prng_dev);
+}
+
+module_init(prng_init);
+module_exit(prng_exit);
diff -urNp linux-2.6.orig/arch/s390/defconfig linux-2.6/arch/s390/defconfig
--- linux-2.6.orig/arch/s390/defconfig 2007-01-12 09:50:41.000000000 +0100
+++ linux-2.6/arch/s390/defconfig 2007-01-16 13:03:12.000000000 +0100
@@ -459,6 +459,7 @@ CONFIG_MONWRITER=m
#
CONFIG_ZCRYPT=m
# CONFIG_ZCRYPT_MONOLITHIC is not set
+CONFIG_S390_PRNG=m
#
# Network device support
diff -urNp linux-2.6.orig/drivers/s390/Kconfig linux-2.6/drivers/s390/Kconfig
--- linux-2.6.orig/drivers/s390/Kconfig 2006-11-17 10:26:36.000000000 +0100
+++ linux-2.6/drivers/s390/Kconfig 2007-01-16 13:03:12.000000000 +0100
@@ -244,4 +244,11 @@ config ZCRYPT_MONOLITHIC
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
+config S390_PRNG
+ tristate "Support for pseudo random number generator device driver"
+ help
+ Select this option if you want to use the s390 pseudo random number generator.
+ The PRNG is part of the cryptograhic processor functions and produces
+ ANSI X9.17 conform numbers. The PRNG is usable via the char device /dev/prandom.
+
endmenu