Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | /* * Copyright IBM Corp. 2006,2007 * Author(s): Jan Glauber <jan.glauber@de.ibm.com> * 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 <jan.glauber@de.ibm.com>"); MODULE_DESCRIPTION("s390 PRNG interface"); static int prng_chunk_size = 256; module_param(prng_chunk_size, int, S_IRUSR | S_IRGRP | S_IROTH); MODULE_PARM_DESC(prng_chunk_size, "PRNG read chunk size in bytes"); static int prng_entropy_limit = 4096; module_param(prng_entropy_limit, int, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR); 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[16]; int i = 0; BUG_ON(nbytes > 16); 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 length, we split 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 const 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 -EOPNOTSUPP; if (prng_chunk_size < 8) return -EINVAL; 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 "Could not register misc device for PRNG.\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); |