/*
* Copyright IBM Corp. 2006, 2015
* Author(s): Jan Glauber <jan.glauber@de.ibm.com>
* Harald Freudenberger <freude@de.ibm.com>
* Driver for the s390 pseudo random number generator
*/
#define KMSG_COMPONENT "prng"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/fs.h>
#include <linux/fips.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/cpufeature.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <asm/debug.h>
#include <asm/uaccess.h>
#include <asm/timex.h>
#include <asm/cpacf.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 PRNG interface");
#define PRNG_MODE_AUTO 0
#define PRNG_MODE_TDES 1
#define PRNG_MODE_SHA512 2
static unsigned int prng_mode = PRNG_MODE_AUTO;
module_param_named(mode, prng_mode, int, 0);
MODULE_PARM_DESC(prng_mode, "PRNG mode: 0 - auto, 1 - TDES, 2 - SHA512");
#define PRNG_CHUNKSIZE_TDES_MIN 8
#define PRNG_CHUNKSIZE_TDES_MAX (64*1024)
#define PRNG_CHUNKSIZE_SHA512_MIN 64
#define PRNG_CHUNKSIZE_SHA512_MAX (64*1024)
static unsigned int prng_chunk_size = 256;
module_param_named(chunksize, prng_chunk_size, int, 0);
MODULE_PARM_DESC(prng_chunk_size, "PRNG read chunk size in bytes");
#define PRNG_RESEED_LIMIT_TDES 4096
#define PRNG_RESEED_LIMIT_TDES_LOWER 4096
#define PRNG_RESEED_LIMIT_SHA512 100000
#define PRNG_RESEED_LIMIT_SHA512_LOWER 10000
static unsigned int prng_reseed_limit;
module_param_named(reseed_limit, prng_reseed_limit, int, 0);
MODULE_PARM_DESC(prng_reseed_limit, "PRNG reseed limit");
/*
* Any one who considers arithmetical methods of producing random digits is,
* of course, in a state of sin. -- John von Neumann
*/
static int prng_errorflag;
#define PRNG_GEN_ENTROPY_FAILED 1
#define PRNG_SELFTEST_FAILED 2
#define PRNG_INSTANTIATE_FAILED 3
#define PRNG_SEED_FAILED 4
#define PRNG_RESEED_FAILED 5
#define PRNG_GEN_FAILED 6
struct prng_ws_s {
u8 parm_block[32];
u32 reseed_counter;
u64 byte_counter;
};
struct ppno_ws_s {
u32 res;
u32 reseed_counter;
u64 stream_bytes;
u8 V[112];
u8 C[112];
};
struct prng_data_s {
struct mutex mutex;
union {
struct prng_ws_s prngws;
struct ppno_ws_s ppnows;
};
u8 *buf;
u32 rest;
u8 *prev;
};
static struct prng_data_s *prng_data;
/* initial parameter block for tdes mode, copied from libica */
static const u8 initial_parm_block[32] __initconst = {
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 };
/*** helper functions ***/
static int generate_entropy(u8 *ebuf, size_t nbytes)
{
int n, ret = 0;
u8 *pg, *h, hash[32];
pg = (u8 *) __get_free_page(GFP_KERNEL);
if (!pg) {
prng_errorflag = PRNG_GEN_ENTROPY_FAILED;
return -ENOMEM;
}
while (nbytes) {
/* fill page with urandom bytes */
get_random_bytes(pg, PAGE_SIZE);
/* exor page with stckf values */
for (n = 0; n < PAGE_SIZE / sizeof(u64); n++) {
u64 *p = ((u64 *)pg) + n;
*p ^= get_tod_clock_fast();
}
n = (nbytes < sizeof(hash)) ? nbytes : sizeof(hash);
if (n < sizeof(hash))
h = hash;
else
h = ebuf;
/* generate sha256 from this page */
if (cpacf_kimd(CPACF_KIMD_SHA_256, h,
pg, PAGE_SIZE) != PAGE_SIZE) {
prng_errorflag = PRNG_GEN_ENTROPY_FAILED;
ret = -EIO;
goto out;
}
if (n < sizeof(hash))
memcpy(ebuf, hash, n);
ret += n;
ebuf += n;
nbytes -= n;
}
out:
free_page((unsigned long)pg);
return ret;
}
/*** tdes functions ***/
static void prng_tdes_add_entropy(void)
{
__u64 entropy[4];
unsigned int i;
int ret;
for (i = 0; i < 16; i++) {
ret = cpacf_kmc(CPACF_KMC_PRNG, prng_data->prngws.parm_block,
(char *)entropy, (char *)entropy,
sizeof(entropy));
BUG_ON(ret < 0 || ret != sizeof(entropy));
memcpy(prng_data->prngws.parm_block, entropy, sizeof(entropy));
}
}
static void prng_tdes_seed(int nbytes)
{
char buf[16];
int i = 0;
BUG_ON(nbytes > sizeof(buf));
get_random_bytes(buf, nbytes);
/* Add the entropy */
while (nbytes >= 8