Add RNG strengthening (10ms once every minute) #15224

pull sipa wants to merge 2 commits into bitcoin:master from sipa:201901_rand_strengthen changing 2 files +56 −4
  1. sipa commented at 9:35 PM on January 21, 2019: member

    This patch improves the built-in RNG using hash strengthening.

    At startup, and once every minute, 32 bytes of entropy are produced from the RNG, repeatedly hashed using SHA512 for 10ms, and then fed back into the RNG, together with high-precision timestamps obtained every 1000 iterations.

  2. hebasto commented at 9:53 PM on January 21, 2019: member

    Is there a rationale for values "10ms" and "1000 iterations"?

  3. sipa force-pushed on Jan 21, 2019
  4. sipa commented at 10:02 PM on January 21, 2019: member

    @hebasto Sort of.

    10ms as it's a amount that shouldn't cause any measurable latency in the background thread (where generally things run that aren't very time critical anyway).

    1000 seems a nice trade-off between time spent on getting the time, and getting entropy out. If we'd get the clock every iteration, we'd be spending more time in reading the clock than actually strenghtening anything. If we'd only get it every 1000000 iterations, we'd probably only read the clock once and not get much entropy from that.

  5. hebasto commented at 10:10 PM on January 21, 2019: member

    Concept ACK.

  6. fanquake added the label Utils/log/libs on Jan 21, 2019
  7. practicalswift commented at 9:53 AM on January 22, 2019: contributor

    @sipa What would be the appropriate way to test/measure the improvement this change brings?

  8. in src/Makefile.am:434 in e20422a6c9 outdated
     430 | @@ -432,6 +431,7 @@ libbitcoin_util_a_SOURCES = \
     431 |    logging.cpp \
     432 |    random.cpp \
     433 |    rpc/protocol.cpp \
     434 | +  shutdown.cpp \
    


    laanwj commented at 11:21 AM on January 22, 2019:

    Moving shutdown.cpp to util might not be a good choice, architecturally: it's shared between all executables, by far most of which don't use bitcond's init/shutdown mechanism. It's meant for general-purpose utilities.

    server would be the right place, it's really part of the server functionality (shared between bitcoind and bitcoin-qt and unit tests…).


    sipa commented at 6:53 PM on January 23, 2019:

    @laanwj Unfortunately, I can't move random to server, because rpc/protocol is in util and depends on random.

    I think I'll just leave the 10ms strengthening uninterruptible instead?


    Sjors commented at 9:55 PM on January 23, 2019:

    I can wait 10ms

  9. in src/random.cpp:472 in e20422a6c9 outdated
     464 | @@ -436,7 +465,23 @@ static void SeedSlow(CSHA512& hasher) noexcept
     465 |      SeedTimestamp(hasher);
     466 |  }
     467 |  
     468 | -static void SeedSleep(CSHA512& hasher)
     469 | +/** Extract entropy from rng, strengthen it, and feed it into hasher. */
     470 | +static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
     471 | +{
     472 | +    // Once per minute, strengthen the hash for 10ms.
     473 | +    static std::atomic<int64_t> last_strengthen;
    


    Sjors commented at 12:30 PM on January 23, 2019:

    Initialize?


    sipa commented at 7:21 PM on January 23, 2019:

    Static variables are always auto-initialized to zero, but for clarity, I've added an explicit zero.

  10. in src/random.cpp:477 in e20422a6c9 outdated
     473 | +    static std::atomic<int64_t> last_strengthen;
     474 | +    int64_t current_time = GetTimeMicros();
     475 | +    if (current_time > last_strengthen + 60000) {
     476 | +        // Generate 32 bytes of entropy from the RNG.
     477 | +        unsigned char buf[32];
     478 | +        rng.MixExtract(buf, 32, CSHA512(), false);
    


    Sjors commented at 12:44 PM on January 23, 2019:

    Nit: might be slightly more readable to rename buf to strengthener_seed, or expand the comment with ", to seed the strengthener."


    sipa commented at 7:21 PM on January 23, 2019:

    Done.

  11. Sjors commented at 12:51 PM on January 23, 2019: member

    Concept ACK, lightly reviewed e20422a. Not seeing any issues except what @laanwj brought up.

  12. sipa force-pushed on Jan 23, 2019
  13. in src/random.cpp:150 in 3a0cf05a99 outdated
     142 | @@ -143,6 +143,34 @@ static bool GetHardwareRand(unsigned char* ent32) noexcept {
     143 |      return false;
     144 |  }
     145 |  
     146 | +/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
     147 | +static void Strengthen(const unsigned char* seed32, int microseconds, CSHA512& hasher) noexcept
     148 | +{
     149 | +    CSHA512 inner_hasher;
     150 | +    inner_hasher.Write(seed32, 32);
    


    gmaxwell commented at 7:06 PM on January 24, 2019:

    Unless I'm misunderstanding it, the data collected by SeedStartup doesn't get strengthened in the first run of the strenghtener. This seems pretty suboptimal since SeedStartup is the obvious place to go get most of the weak pseudo-entropy (like filesystem data, network interface data, etc.) which is the stuff that most benefits from strengthening.


    sipa commented at 7:55 PM on January 24, 2019:

    Good point, fixed.

    It now extracts using the hasher state combined with the RNG.

    So overall what happens in SeedStartup is:

    • Gather entropy using SeedSlow/RandAddSeedPerfmon, in a hasher E. E = SeedStuff()
    • Mix E together with the (in this case empty RNG state R0), and split the result in a seed and the new RNG state, so [S,R1] = SHA512(E || R0)
    • S gets strengthened, S' = SHA512(SHA512(...(S)))
    • S' is added to E, and mixed into the RNG: [_,R2] = SHA512(E || S' || R1).

    It's a bit weird in that it uses E both in construction R1 and R2, but I don't see how that can hurt.

  14. gmaxwell commented at 7:08 PM on January 24, 2019: contributor

    I'd still prefer to see this using 4x SHA256^2-64b since that function has a much better ratio of current performance to attacker performance, but I grok the startup time interactions with function auto-detection make that messy. I'm okay with using sha512 for now in light of that!

  15. sipa force-pushed on Jan 24, 2019
  16. gmaxwell commented at 12:25 AM on January 25, 2019: contributor

    We should contemplate increasing the strenghtening time on the first run. Rationale: usually long term private keys will get generated within one minute of first startup when only the startup strengthening will have applied. Adding an extra 90ms on startup won't be significantly noticeable on top of the existing startup time, and would increase resistance to search by 10x. Alternative: Once we've started carrying randomness across restarts in a file, a prolonged startup strengthen would only be needed when there is no saved randomness.

  17. in src/random.cpp:476 in f20fbecded outdated
     472 | +    static std::atomic<int64_t> last_strengthen{0};
     473 | +    int64_t current_time = GetTimeMicros();
     474 | +    if (current_time > last_strengthen + 60000) {
     475 | +        // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
     476 | +        unsigned char strengthen_seed[32];
     477 | +        rng.MixExtract(strengthen_seed, 32, CSHA512(hasher), false);
    


    Empact commented at 1:15 AM on January 25, 2019:

    nit: sizeof(strengthen_seed) would avoid the duplication here.


    sipa commented at 11:01 PM on May 3, 2019:

    Fixed.

  18. in src/random.cpp:147 in f20fbecded outdated
     142 | @@ -143,6 +143,34 @@ static bool GetHardwareRand(unsigned char* ent32) noexcept {
     143 |      return false;
     144 |  }
     145 |  
     146 | +/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
     147 | +static void Strengthen(const unsigned char* seed32, int microseconds, CSHA512& hasher) noexcept
    


    Empact commented at 1:17 AM on January 25, 2019:

    nit: passing in seed32's length could be less error prone, in that the function would make fewer assumptions about the arguments / have fewer preconditions for them. Could be good to document the size precondition in the comment as an alternative.


    sipa commented at 10:55 PM on May 3, 2019:

    Fixed by using the C++11 "pass a reference to array of fixed size" construction (which will cause a compilation failure of the array has the wrong length).

  19. laanwj added this to the milestone 0.19.0 on Feb 16, 2019
  20. sipa force-pushed on May 3, 2019
  21. sipa force-pushed on May 3, 2019
  22. sipa commented at 11:02 PM on May 3, 2019: member

    Adressed @Empact's nits, and made the first strengthening run 100ms long. Also fixed a bug where it would run every 60 ms instead of every minute...

  23. gmaxwell commented at 12:39 AM on May 6, 2019: contributor

    utACK

  24. in src/random.cpp:154 in c5b36592ea outdated
     149 | +    CSHA512 inner_hasher;
     150 | +    inner_hasher.Write(seed, sizeof(seed));
     151 | +
     152 | +    // Hash loop
     153 | +    unsigned char buffer[64];
     154 | +    int64_t start = GetTimeMicros();
    


    promag commented at 5:41 PM on May 6, 2019:

    nit const int64_t stop = GetTimeMicros() + microseconds;


    sipa commented at 10:22 PM on May 6, 2019:

    Nice, done.

  25. in src/random.cpp:169 in c5b36592ea outdated
     164 | +    } while (GetTimeMicros() < start + microseconds);
     165 | +
     166 | +    // Produce output from inner state and feed it to outer hasher.
     167 | +    inner_hasher.Finalize(buffer);
     168 | +    hasher.Write(buffer, sizeof(buffer));
     169 | +    // Try to clean up.
    


    promag commented at 5:44 PM on May 6, 2019:

    Can you explain why "try"?


    sipa commented at 10:13 PM on May 6, 2019:

    The C++ language has no way of guaranteeing memory gets cleaned up (it may have copied it elsewhere in memory, for starters, or detect that the clearing has no observable effect and optimize it away).

  26. Add hash strengthening to the RNG
    Once every minute, this will feed the RNG state through repeated SHA512
    for 10ms. The timings of that operation are used as entropy source as
    well.
    1d207bc46f
  27. Document strenghtening 3cb9ce85d0
  28. sipa force-pushed on May 6, 2019
  29. laanwj added this to the "Blockers" column in a project

  30. pstratem commented at 12:50 AM on May 18, 2019: contributor

    utACK 3cb9ce85d0c6d01217babf0df7efc2eabde1b12f

  31. laanwj merged this on May 18, 2019
  32. laanwj closed this on May 18, 2019

  33. laanwj referenced this in commit 82b64a5a81 on May 18, 2019
  34. sidhujag referenced this in commit 3e20aca358 on May 18, 2019
  35. laanwj removed this from the "Blockers" column in a project

  36. MarcoFalke commented at 3:50 PM on May 20, 2019: member
  37. sipa commented at 11:33 PM on May 20, 2019: member

    @MarcoFalke Interesting. Is that considered too much? We could disable the strengthening time, or make it configurable... though at the cost of some complexity (the RNG may be used before command line options are parsed...)

  38. MarcoFalke commented at 11:13 AM on May 21, 2019: member

    Not really. Just mentioned because it might be interesting to some.

  39. deadalnix referenced this in commit fd5dffcbe5 on May 19, 2020
  40. random-zebra referenced this in commit 93f43f0f81 on Apr 14, 2021
  41. PastaPastaPasta referenced this in commit 38b06d6857 on Sep 14, 2021
  42. PastaPastaPasta referenced this in commit e54ac3a2cf on Sep 14, 2021
  43. PastaPastaPasta referenced this in commit f1c784ffc3 on Sep 15, 2021
  44. MarcoFalke locked this on Dec 16, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-13 15:15 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me