2

I was just reviewing the source-code of the NBitcoin-package. As far as i can see when you create a new bitcoin-keypair

Key privateKey = new Key();

(without passing some random data to be used as private key generated by yourself) then the library generates a private key using a random-number-generator for you. Very usual. But then i stumbled across the fact that the output of the random-number-generator will not directly be used as private key: NBitcoin also applies a PushEntropy-function on this byte-array:

private static void PushEntropy(byte[] data)
{
    if (!UseAdditionalEntropy || additionalEntropy == null || data.Length == 0)
        return;
    int pos = entropyIndex;
    var entropy = additionalEntropy;
    for (int i = 0; i < data.Length; i++)
    {
        data[i] ^= entropy[pos % 32];
        pos++;
    }
    entropy = Hashes.SHA256(data);
    for (int i = 0; i < data.Length; i++)
    {
        data[i] ^= entropy[pos % 32];
        pos++;
    }
    entropyIndex = pos % 32;
}

I understand what this function does, but I do not understand why we need this function to generate a private-key.

Why is a cryptographically secure random-number-generator (to generate the private key aka data-bytearray) not enough?

Does this function increase security in any kind?

anion
  • 123
  • 3
  • We had a similar question, need to be found. It is a little failsafe if there is some problem on the random-number-generator ( there are/were lots of systems with poor entropy and bugs) the hash can randomize it though cannot change the entropy of the source. – kelalaka Apr 17 '21 at 19:16

1 Answers1

2

Why is a cryptographically secure random-number-generator (to generate the private key aka data-bytearray) not enough?

A Cryptographically Secure True Random Number Generator would be enough. This code uses a belt-and-suspenders approach. This is common in RNGs, in recognition of the fact that RNGs are traditionally one of the most brittle components in crypto implementations (see e.g.SmartFacts).

The intention appears to be: if UseAdditionalEntropy is at it's default of true, and additionalEntropy has been set with something unguessable or/and truly random by calling one of two forms of AddEntropy

  1. Try to save the day if initially data contains something guessable, by mixing the two (in the first loop)
  2. Make it hard to "walk back" from the result, by applying a non-reversible transformation. When data is 32-byte and entropyIndex starts at zero, this transformation is $\mathtt{data}\mapsto\operatorname{SHA-256}(\mathtt{data})\oplus\mathtt{data}$. Given the output, the only hope to find the input is by enumeration of likely input values¹.

Step 1 can conceivably backfire, e.g. if data passed is the SHA-256 hash of what was previously passed to AddEntropy.

Step 2 slightly reduces the entropy (by less than 1 bit if data was full-entropy, see this; and negligibly so if data was less than full-entropy), but in the context that's fine.

Does this function increase security in any kind?

It does, if Random.GetBytes of the calling context has a flaw, and AddEntropy was used and passed something secret (including something random and independent of what Random.GetBytes outputs; or just a secret constant generated at random at time of installation). As long as PushEntropy, additionalEntropy and entropyIndex remain private (thus not accidentally callable), this precaution is unlikely to accidentally backfire.


¹ If we changed += to ^= in the final mixing step of the round function of SHA-256, this transformation would be reversible. And it would be dangerously close to reversible if we changed ^= to -= in the code shown.

fgrieu
  • 140,762
  • 12
  • 307
  • 587