Re: [PATCH] arch/s390/crypto/prng: Stop being stupidly wasteful with /dev/urandom

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello George

based on your suggestions here is a rework of the s390 prandom dd.

Can you please have a look into ? Thanks for your suggestions

regards
Harald Freudenberger
----------------------------------------------------------------------
>From a9864786f32e71df7a727a912c76cfe4ca96df6a Mon Sep 17 00:00:00 2001
From: Harald Freudenberger <freude@xxxxxxxxxxxxx>
Date: Thu, 11 Apr 2019 15:47:34 +0200
Subject: [PATCH] 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.

The rework addresses these two points and now a buffer of
1024 bytes is used which is only initial filled with
get_random_bytes(). Also the stckf values are now exored
into the buffer with overlap of 7 bytes, so effectively each
value only consumes 1 byte but the entropy is preserved.
For more details about the algorithm used, see the header
of the function.

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: Harald Freudenberger <freude@xxxxxxxxxxxxx>
Reported-by: George Spelvin <lkml@xxxxxxx>
Suggested-by: George Spelvin <lkml@xxxxxxx>
---
 arch/s390/crypto/prng.c | 64 +++++++++++++++++++++++++++--------------
 1 file changed, 43 insertions(+), 21 deletions(-)

diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c
index a97a1802cfb4..530c836e3e03 100644
--- a/arch/s390/crypto/prng.c
+++ b/arch/s390/crypto/prng.c
@@ -115,29 +115,41 @@ static const u8 initial_parm_block[32] __initconst = {
 
 /*
  * generate_entropy:
- * This algorithm produces 64 bytes of entropy data based on 1024
- * individual stckf() invocations assuming that each stckf() value
- * contributes 0.25 bits of entropy. So the caller gets 256 bit
- * entropy per 64 byte or 4 bits entropy per byte.
+ * This function fills a given buffer with random bytes. The entropy within
+ * the random bytes given back is assumed to have at least 50% - meaning
+ * a 64 bytes buffer has at least 64 * 8 / 2 = 256 bits of entropy.
+ * Within the function the entropy generation is done in junks of 64 bytes.
+ * So the calling function 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. So to accumulate 256 bits of entropy
+ * at least 512 stckf() values are needed. This function uses a 1K buffer which
+ * is initially filled with get_random_bytes() and then 1024 stckf values are
+ * exored on the buffer with 1 byte offset each. So the entropy from the 1024
+ * stckf values is spread over the 1K buffer. Finally the buffer is condensed
+ * into a 64 bytes value by hashing with a SHA512.
  */
 static int generate_entropy(u8 *ebuf, size_t nbytes)
 {
     int n, ret = 0;
-    u8 *pg, *h, hash[64];
+    u8 *buf, *h, hash[64];
+    const int bufsize = 1024;
 
-    /* allocate 2 pages */
-    pg = (u8 *) __get_free_pages(GFP_KERNEL, 1);
-    if (!pg) {
+    /* allocate stckf buffer space */
+    buf = kmalloc(bufsize, GFP_KERNEL);
+    if (!buf) {
         prng_errorflag = PRNG_GEN_ENTROPY_FAILED;
         return -ENOMEM;
     }
 
+    /* initialy fill the stckf buffer with random data */
+    get_random_bytes(buf, bufsize);
+
+    /* now fill the ebuf in chunks of 64 byte each */
     while (nbytes) {
-        /* fill pages with urandom bytes */
-        get_random_bytes(pg, 2*PAGE_SIZE);
-        /* exor pages with 1024 stckf values */
-        for (n = 0; n < 2 * PAGE_SIZE / sizeof(u64); n++) {
-            u64 *p = ((u64 *)pg) + n;
+        /* exor stckf values into the buffer with 1 byte offset each */
+        for (n = 0; n < bufsize - sizeof(u64); n++) {
+            u64 *p = (u64 *)(buf + n);
             *p ^= get_tod_clock_fast();
         }
         n = (nbytes < sizeof(hash)) ? nbytes : sizeof(hash);
@@ -145,8 +157,8 @@ static int generate_entropy(u8 *ebuf, size_t nbytes)
             h = hash;
         else
             h = ebuf;
-        /* hash over the filled pages */
-        cpacf_kimd(CPACF_KIMD_SHA_512, h, pg, 2*PAGE_SIZE);
+        /* hash over the filled pages (no need to init hash here) */
+        cpacf_kimd(CPACF_KIMD_SHA_512, h, buf, bufsize);
         if (n < sizeof(hash))
             memcpy(ebuf, hash, n);
         ret += n;
@@ -154,7 +166,9 @@ static int generate_entropy(u8 *ebuf, size_t nbytes)
         nbytes -= n;
     }
 
-    free_pages((unsigned long)pg, 1);
+    memzero_explicit(hash, sizeof(hash));
+    memzero_explicit(buf, bufsize);
+    kfree(buf);
     return ret;
 }
 
@@ -345,7 +359,7 @@ static int __init prng_sha512_selftest(void)
 static int __init prng_sha512_instantiate(void)
 {
     int ret, datalen;
-    u8 seed[64 + 32 + 16];
+    u8 seed[128 + 16];
 
     pr_debug("prng runs in SHA-512 mode "
          "with chunksize=%d and reseed_limit=%u\n",
@@ -368,16 +382,22 @@ static int __init prng_sha512_instantiate(void)
     if (ret)
         goto outfree;
 
-    /* generate initial seed bytestring, with 256 + 128 bits entropy */
-    ret = generate_entropy(seed, 64 + 32);
-    if (ret != 64 + 32)
+    /*
+     * generate initial seed bytestring, we need at least
+     * 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;
     /* 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 */
     cpacf_prno(CPACF_PRNO_SHA512_DRNG_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
        bytes for the FIPS 140-2 Conditional Self Test */
@@ -416,6 +436,7 @@ static int prng_sha512_reseed(void)
     /* do a reseed of the prno drng with this bytestring */
     cpacf_prno(CPACF_PRNO_SHA512_DRNG_SEED,
            &prng_data->prnows, NULL, 0, seed, sizeof(seed));
+    memzero_explicit(seed, sizeof(seed));
 
     return 0;
 }
@@ -592,6 +613,7 @@ static ssize_t prng_sha512_read(struct file *file, char __user *ubuf,
             ret = -EFAULT;
             break;
         }
+        memzero_explicit(p, n);
         ubuf += n;
         nbytes -= n;
         ret += n;
-- 
2.17.1




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Kernel Development]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Info]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Linux Media]     [Device Mapper]

  Powered by Linux