diff options
Diffstat (limited to 'crypto/prng.c')
-rw-r--r-- | crypto/prng.c | 410 |
1 files changed, 0 insertions, 410 deletions
diff --git a/crypto/prng.c b/crypto/prng.c deleted file mode 100644 index 24e4f3282c5..00000000000 --- a/crypto/prng.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * PRNG: Pseudo Random Number Generator - * Based on NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4 using - * AES 128 cipher in RFC3686 ctr mode - * - * (C) Neil Horman <nhorman@tuxdriver.com> - * - * 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 - * any later version. - * - * - */ - -#include <linux/err.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/scatterlist.h> -#include <linux/string.h> -#include <linux/crypto.h> -#include <linux/highmem.h> -#include <linux/moduleparam.h> -#include <linux/jiffies.h> -#include <linux/timex.h> -#include <linux/interrupt.h> -#include <linux/miscdevice.h> -#include "prng.h" - -#define TEST_PRNG_ON_START 0 - -#define DEFAULT_PRNG_KEY "0123456789abcdef1011" -#define DEFAULT_PRNG_KSZ 20 -#define DEFAULT_PRNG_IV "defaultv" -#define DEFAULT_PRNG_IVSZ 8 -#define DEFAULT_BLK_SZ 16 -#define DEFAULT_V_SEED "zaybxcwdveuftgsh" - -/* - * Flags for the prng_context flags field - */ - -#define PRNG_FIXED_SIZE 0x1 -#define PRNG_NEED_RESET 0x2 - -/* - * Note: DT is our counter value - * I is our intermediate value - * V is our seed vector - * See http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf - * for implementation details - */ - - -struct prng_context { - char *prng_key; - char *prng_iv; - spinlock_t prng_lock; - unsigned char rand_data[DEFAULT_BLK_SZ]; - unsigned char last_rand_data[DEFAULT_BLK_SZ]; - unsigned char DT[DEFAULT_BLK_SZ]; - unsigned char I[DEFAULT_BLK_SZ]; - unsigned char V[DEFAULT_BLK_SZ]; - u32 rand_data_valid; - struct crypto_blkcipher *tfm; - u32 flags; -}; - -static int dbg; - -static void hexdump(char *note, unsigned char *buf, unsigned int len) -{ - if (dbg) { - printk(KERN_CRIT "%s", note); - print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET, - 16, 1, - buf, len, false); - } -} - -#define dbgprint(format, args...) do {if(dbg) printk(format, ##args);} while(0) - -static void xor_vectors(unsigned char *in1, unsigned char *in2, - unsigned char *out, unsigned int size) -{ - int i; - - for (i=0;i<size;i++) - out[i] = in1[i] ^ in2[i]; - -} -/* - * Returns DEFAULT_BLK_SZ bytes of random data per call - * returns 0 if generation succeded, <0 if something went wrong - */ -static int _get_more_prng_bytes(struct prng_context *ctx) -{ - int i; - struct blkcipher_desc desc; - struct scatterlist sg_in, sg_out; - int ret; - unsigned char tmp[DEFAULT_BLK_SZ]; - - desc.tfm = ctx->tfm; - desc.flags = 0; - - - dbgprint(KERN_CRIT "Calling _get_more_prng_bytes for context %p\n",ctx); - - hexdump("Input DT: ", ctx->DT, DEFAULT_BLK_SZ); - hexdump("Input I: ", ctx->I, DEFAULT_BLK_SZ); - hexdump("Input V: ", ctx->V, DEFAULT_BLK_SZ); - - /* - * This algorithm is a 3 stage state machine - */ - for (i=0;i<3;i++) { - - desc.tfm = ctx->tfm; - desc.flags = 0; - switch (i) { - case 0: - /* - * Start by encrypting the counter value - * This gives us an intermediate value I - */ - memcpy(tmp, ctx->DT, DEFAULT_BLK_SZ); - sg_init_one(&sg_out, &ctx->I[0], DEFAULT_BLK_SZ); - hexdump("tmp stage 0: ", tmp, DEFAULT_BLK_SZ); - break; - case 1: - - /* - * Next xor I with our secret vector V - * encrypt that result to obtain our - * pseudo random data which we output - */ - xor_vectors(ctx->I, ctx->V, tmp, DEFAULT_BLK_SZ); - sg_init_one(&sg_out, &ctx->rand_data[0], DEFAULT_BLK_SZ); - hexdump("tmp stage 1: ", tmp, DEFAULT_BLK_SZ); - break; - case 2: - /* - * First check that we didn't produce the same random data - * that we did last time around through this - */ - if (!memcmp(ctx->rand_data, ctx->last_rand_data, DEFAULT_BLK_SZ)) { - printk(KERN_ERR "ctx %p Failed repetition check!\n", - ctx); - ctx->flags |= PRNG_NEED_RESET; - return -1; - } - memcpy(ctx->last_rand_data, ctx->rand_data, DEFAULT_BLK_SZ); - - /* - * Lastly xor the random data with I - * and encrypt that to obtain a new secret vector V - */ - xor_vectors(ctx->rand_data, ctx->I, tmp, DEFAULT_BLK_SZ); - sg_init_one(&sg_out, &ctx->V[0], DEFAULT_BLK_SZ); - hexdump("tmp stage 2: ", tmp, DEFAULT_BLK_SZ); - break; - } - - /* Initialize our input buffer */ - sg_init_one(&sg_in, &tmp[0], DEFAULT_BLK_SZ); - - /* do the encryption */ - ret = crypto_blkcipher_encrypt(&desc, &sg_out, &sg_in, DEFAULT_BLK_SZ); - - /* And check the result */ - if (ret) { - dbgprint(KERN_CRIT "Encryption of new block failed for context %p\n",ctx); - ctx->rand_data_valid = DEFAULT_BLK_SZ; - return -1; - } - - } - - /* - * Now update our DT value - */ - for (i=DEFAULT_BLK_SZ-1;i>0;i--) { - ctx->DT[i] = ctx->DT[i-1]; - } - ctx->DT[0] += 1; - - dbgprint("Returning new block for context %p\n",ctx); - ctx->rand_data_valid = 0; - - hexdump("Output DT: ", ctx->DT, DEFAULT_BLK_SZ); - hexdump("Output I: ", ctx->I, DEFAULT_BLK_SZ); - hexdump("Output V: ", ctx->V, DEFAULT_BLK_SZ); - hexdump("New Random Data: ", ctx->rand_data, DEFAULT_BLK_SZ); - - return 0; -} - -/* Our exported functions */ -int get_prng_bytes(char *buf, int nbytes, struct prng_context *ctx) -{ - unsigned long flags; - unsigned char *ptr = buf; - unsigned int byte_count = (unsigned int)nbytes; - int err; - - - if (nbytes < 0) - return -EINVAL; - - spin_lock_irqsave(&ctx->prng_lock, flags); - - err = -EFAULT; - if (ctx->flags & PRNG_NEED_RESET) - goto done; - - /* - * If the FIXED_SIZE flag is on, only return whole blocks of - * pseudo random data - */ - err = -EINVAL; - if (ctx->flags & PRNG_FIXED_SIZE) { - if (nbytes < DEFAULT_BLK_SZ) - goto done; - byte_count = DEFAULT_BLK_SZ; - } - - err = byte_count; - - dbgprint(KERN_CRIT "getting %d random bytes for context %p\n",byte_count, ctx); - - -remainder: - if (ctx->rand_data_valid == DEFAULT_BLK_SZ) { - if (_get_more_prng_bytes(ctx) < 0) { - memset(buf, 0, nbytes); - err = -EFAULT; - goto done; - } - } - - /* - * Copy up to the next whole block size - */ - if (byte_count < DEFAULT_BLK_SZ) { - for (;ctx->rand_data_valid < DEFAULT_BLK_SZ; ctx->rand_data_valid++) { - *ptr = ctx->rand_data[ctx->rand_data_valid]; - ptr++; - byte_count--; - if (byte_count == 0) - goto done; - } - } - - /* - * Now copy whole blocks - */ - for(;byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) { - if (_get_more_prng_bytes(ctx) < 0) { - memset(buf, 0, nbytes); - err = -1; - goto done; - } - memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ); - ctx->rand_data_valid += DEFAULT_BLK_SZ; - ptr += DEFAULT_BLK_SZ; - } - - /* - * Now copy any extra partial data - */ - if (byte_count) - goto remainder; - -done: - spin_unlock_irqrestore(&ctx->prng_lock, flags); - dbgprint(KERN_CRIT "returning %d from get_prng_bytes in context %p\n",err, ctx); - return err; -} -EXPORT_SYMBOL_GPL(get_prng_bytes); - -struct prng_context *alloc_prng_context(void) -{ - struct prng_context *ctx=kzalloc(sizeof(struct prng_context), GFP_KERNEL); - - spin_lock_init(&ctx->prng_lock); - - if (reset_prng_context(ctx, NULL, NULL, NULL, NULL)) { - kfree(ctx); - ctx = NULL; - } - - dbgprint(KERN_CRIT "returning context %p\n",ctx); - return ctx; -} - -EXPORT_SYMBOL_GPL(alloc_prng_context); - -void free_prng_context(struct prng_context *ctx) -{ - crypto_free_blkcipher(ctx->tfm); - kfree(ctx); -} -EXPORT_SYMBOL_GPL(free_prng_context); - -int reset_prng_context(struct prng_context *ctx, - unsigned char *key, unsigned char *iv, - unsigned char *V, unsigned char *DT) -{ - int ret; - int iv_len; - int rc = -EFAULT; - - spin_lock(&ctx->prng_lock); - ctx->flags |= PRNG_NEED_RESET; - - if (key) - memcpy(ctx->prng_key,key,strlen(ctx->prng_key)); - else - ctx->prng_key = DEFAULT_PRNG_KEY; - - if (iv) - memcpy(ctx->prng_iv,iv, strlen(ctx->prng_iv)); - else - ctx->prng_iv = DEFAULT_PRNG_IV; - - if (V) - memcpy(ctx->V,V,DEFAULT_BLK_SZ); - else - memcpy(ctx->V,DEFAULT_V_SEED,DEFAULT_BLK_SZ); - - if (DT) - memcpy(ctx->DT, DT, DEFAULT_BLK_SZ); - else - memset(ctx->DT, 0, DEFAULT_BLK_SZ); - - memset(ctx->rand_data,0,DEFAULT_BLK_SZ); - memset(ctx->last_rand_data,0,DEFAULT_BLK_SZ); - - if (ctx->tfm) - crypto_free_blkcipher(ctx->tfm); - - ctx->tfm = crypto_alloc_blkcipher("rfc3686(ctr(aes))",0,0); - if (!ctx->tfm) { - dbgprint(KERN_CRIT "Failed to alloc crypto tfm for context %p\n",ctx->tfm); - goto out; - } - - ctx->rand_data_valid = DEFAULT_BLK_SZ; - - ret = crypto_blkcipher_setkey(ctx->tfm, ctx->prng_key, strlen(ctx->prng_key)); - if (ret) { - dbgprint(KERN_CRIT "PRNG: setkey() failed flags=%x\n", - crypto_blkcipher_get_flags(ctx->tfm)); - crypto_free_blkcipher(ctx->tfm); - goto out; - } - - iv_len = crypto_blkcipher_ivsize(ctx->tfm); - if (iv_len) { - crypto_blkcipher_set_iv(ctx->tfm, ctx->prng_iv, iv_len); - } - rc = 0; - ctx->flags &= ~PRNG_NEED_RESET; -out: - spin_unlock(&ctx->prng_lock); - - return rc; - -} -EXPORT_SYMBOL_GPL(reset_prng_context); - -/* Module initalization */ -static int __init prng_mod_init(void) -{ - -#ifdef TEST_PRNG_ON_START - int i; - unsigned char tmpbuf[DEFAULT_BLK_SZ]; - - struct prng_context *ctx = alloc_prng_context(); - if (ctx == NULL) - return -EFAULT; - for (i=0;i<16;i++) { - if (get_prng_bytes(tmpbuf, DEFAULT_BLK_SZ, ctx) < 0) { - free_prng_context(ctx); - return -EFAULT; - } - } - free_prng_context(ctx); -#endif - - return 0; -} - -static void __exit prng_mod_fini(void) -{ - return; -} - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Software Pseudo Random Number Generator"); -MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>"); -module_param(dbg, int, 0); -MODULE_PARM_DESC(dbg, "Boolean to enable debugging (0/1 == off/on)"); -module_init(prng_mod_init); -module_exit(prng_mod_fini); |