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;


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to