Commit 23d1aee9 authored by Harald Freudenberger's avatar Harald Freudenberger Committed by Martin Schwidefsky

s390/crypto: rework generate_entropy function for pseudo random dd

Here is a rework of the generate_entropy function of the pseudo random
device driver exploiting the prno CPACF instruction.

George Spelvin pointed out some issues with the existing
implementation. One point was, that the buffer used to store the stckf
values is 2 pages which are initially filled with get_random_bytes()
for each 64 byte junk produced by the function. Another point was that
the stckf values only carry entropy in the LSB and thus a buffer of
2 pages is not really needed. Then there was a comment about the use
of the kimd cpacf function without proper initialization.

The rework addresses these points and now one page is used and only
one half of this is filled with get_random_bytes() on each chunk of 64
bytes requested data. The other half of the page is filled with stckf
values exored into with an overlap of 4 bytes. This can be done due to
the fact that only the lower 4 bytes carry entropy we need.  For more
details about the algorithm used, see the header of the function.

The generate_entropy() function now uses the cpacf function klmd with
proper initialization of the parameter block to perform the sha512
hash.

George also pointed out some issues with the internal buffers used for
seeding and reads. These buffers are now zeroed with memzero_implicit
after use.
Signed-off-by: default avatarHarald Freudenberger <freude@linux.ibm.com>
Reported-by: default avatarGeorge Spelvin <lkml@sdf.org>
Suggested-by: default avatarGeorge Spelvin <lkml@sdf.org>
Reviewed-by: default avatarPatrick Steuer <steuer@linux.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 5513bc8e
...@@ -115,46 +115,68 @@ static const u8 initial_parm_block[32] __initconst = { ...@@ -115,46 +115,68 @@ static const u8 initial_parm_block[32] __initconst = {
/* /*
* generate_entropy: * generate_entropy:
* This algorithm produces 64 bytes of entropy data based on 1024 * This function fills a given buffer with random bytes. The entropy within
* individual stckf() invocations assuming that each stckf() value * the random bytes given back is assumed to have at least 50% - meaning
* contributes 0.25 bits of entropy. So the caller gets 256 bit * a 64 bytes buffer has at least 64 * 8 / 2 = 256 bits of entropy.
* entropy per 64 byte or 4 bits entropy per byte. * Within the function the entropy generation is done in junks of 64 bytes.
* So the caller should also ask for buffer fill in multiples of 64 bytes.
* The generation of the entropy is based on the assumption that every stckf()
* invocation produces 0.5 bits of entropy. To accumulate 256 bits of entropy
* at least 512 stckf() values are needed. The entropy relevant part of the
* stckf value is bit 51 (counting starts at the left with bit nr 0) so
* here we use the lower 4 bytes and exor the values into 2k of bufferspace.
* To be on the save side, if there is ever a problem with stckf() the
* other half of the page buffer is filled with bytes from urandom via
* get_random_bytes(), so this function consumes 2k of urandom for each
* requested 64 bytes output data. Finally the buffer page is condensed into
* a 64 byte value by hashing with a SHA512 hash.
*/ */
static int generate_entropy(u8 *ebuf, size_t nbytes) static int generate_entropy(u8 *ebuf, size_t nbytes)
{ {
int n, ret = 0; int n, ret = 0;
u8 *pg, *h, hash[64]; u8 *pg, pblock[80] = {
/* 8 x 64 bit init values */
/* allocate 2 pages */ 0x6A, 0x09, 0xE6, 0x67, 0xF3, 0xBC, 0xC9, 0x08,
pg = (u8 *) __get_free_pages(GFP_KERNEL, 1); 0xBB, 0x67, 0xAE, 0x85, 0x84, 0xCA, 0xA7, 0x3B,
0x3C, 0x6E, 0xF3, 0x72, 0xFE, 0x94, 0xF8, 0x2B,
0xA5, 0x4F, 0xF5, 0x3A, 0x5F, 0x1D, 0x36, 0xF1,
0x51, 0x0E, 0x52, 0x7F, 0xAD, 0xE6, 0x82, 0xD1,
0x9B, 0x05, 0x68, 0x8C, 0x2B, 0x3E, 0x6C, 0x1F,
0x1F, 0x83, 0xD9, 0xAB, 0xFB, 0x41, 0xBD, 0x6B,
0x5B, 0xE0, 0xCD, 0x19, 0x13, 0x7E, 0x21, 0x79,
/* 128 bit counter total message bit length */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00 };
/* allocate one page stckf buffer */
pg = (u8 *) __get_free_page(GFP_KERNEL);
if (!pg) { if (!pg) {
prng_errorflag = PRNG_GEN_ENTROPY_FAILED; prng_errorflag = PRNG_GEN_ENTROPY_FAILED;
return -ENOMEM; return -ENOMEM;
} }
/* fill the ebuf in chunks of 64 byte each */
while (nbytes) { while (nbytes) {
/* fill pages with urandom bytes */ /* fill lower 2k with urandom bytes */
get_random_bytes(pg, 2*PAGE_SIZE); get_random_bytes(pg, PAGE_SIZE / 2);
/* exor pages with 1024 stckf values */ /* exor upper 2k with 512 stckf values, offset 4 bytes each */
for (n = 0; n < 2 * PAGE_SIZE / sizeof(u64); n++) { for (n = 0; n < 512; n++) {
u64 *p = ((u64 *)pg) + n; int offset = (PAGE_SIZE / 2) + (n * 4) - 4;
u64 *p = (u64 *)(pg + offset);
*p ^= get_tod_clock_fast(); *p ^= get_tod_clock_fast();
} }
n = (nbytes < sizeof(hash)) ? nbytes : sizeof(hash); /* hash over the filled page */
if (n < sizeof(hash)) cpacf_klmd(CPACF_KLMD_SHA_512, pblock, pg, PAGE_SIZE);
h = hash; n = (nbytes < 64) ? nbytes : 64;
else memcpy(ebuf, pblock, n);
h = ebuf;
/* hash over the filled pages */
cpacf_kimd(CPACF_KIMD_SHA_512, h, pg, 2*PAGE_SIZE);
if (n < sizeof(hash))
memcpy(ebuf, hash, n);
ret += n; ret += n;
ebuf += n; ebuf += n;
nbytes -= n; nbytes -= n;
} }
free_pages((unsigned long)pg, 1); memzero_explicit(pblock, sizeof(pblock));
memzero_explicit(pg, PAGE_SIZE);
free_page((unsigned long)pg);
return ret; return ret;
} }
...@@ -345,7 +367,7 @@ static int __init prng_sha512_selftest(void) ...@@ -345,7 +367,7 @@ static int __init prng_sha512_selftest(void)
static int __init prng_sha512_instantiate(void) static int __init prng_sha512_instantiate(void)
{ {
int ret, datalen; int ret, datalen;
u8 seed[64 + 32 + 16]; u8 seed[128 + 16];
pr_debug("prng runs in SHA-512 mode " pr_debug("prng runs in SHA-512 mode "
"with chunksize=%d and reseed_limit=%u\n", "with chunksize=%d and reseed_limit=%u\n",
...@@ -368,16 +390,22 @@ static int __init prng_sha512_instantiate(void) ...@@ -368,16 +390,22 @@ static int __init prng_sha512_instantiate(void)
if (ret) if (ret)
goto outfree; goto outfree;
/* generate initial seed bytestring, with 256 + 128 bits entropy */ /*
ret = generate_entropy(seed, 64 + 32); * generate initial seed bytestring, we need at least
if (ret != 64 + 32) * 256 + 128 bits entropy. However, the generate_entropy()
* function anyway works in 64 byte junks so we pull
* 2*64 bytes here.
*/
ret = generate_entropy(seed, 128);
if (ret != 128)
goto outfree; goto outfree;
/* followed by 16 bytes of unique nonce */ /* followed by 16 bytes of unique nonce */
get_tod_clock_ext(seed + 64 + 32); get_tod_clock_ext(seed + 128);
/* initial seed of the prno drng */ /* initial seed of the prno drng */
cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED,
&prng_data->prnows, NULL, 0, seed, sizeof(seed)); &prng_data->prnows, NULL, 0, seed, sizeof(seed));
memzero_explicit(seed, sizeof(seed));
/* if fips mode is enabled, generate a first block of random /* if fips mode is enabled, generate a first block of random
bytes for the FIPS 140-2 Conditional Self Test */ bytes for the FIPS 140-2 Conditional Self Test */
...@@ -416,6 +444,7 @@ static int prng_sha512_reseed(void) ...@@ -416,6 +444,7 @@ static int prng_sha512_reseed(void)
/* do a reseed of the prno drng with this bytestring */ /* do a reseed of the prno drng with this bytestring */
cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED, cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED,
&prng_data->prnows, NULL, 0, seed, sizeof(seed)); &prng_data->prnows, NULL, 0, seed, sizeof(seed));
memzero_explicit(seed, sizeof(seed));
return 0; return 0;
} }
...@@ -592,6 +621,7 @@ static ssize_t prng_sha512_read(struct file *file, char __user *ubuf, ...@@ -592,6 +621,7 @@ static ssize_t prng_sha512_read(struct file *file, char __user *ubuf,
ret = -EFAULT; ret = -EFAULT;
break; break;
} }
memzero_explicit(p, n);
ubuf += n; ubuf += n;
nbytes -= n; nbytes -= n;
ret += n; ret += n;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment