1

Background

I do not recommend trying to roll your own crypto. This is just a for-fun PoC, which won't be used in any public or private scenario.

I am creating a PoC nearly-true random number generator, but this won't be used in real applications. I know the rule of "Don't roll your own crypto", but this is just a fun PoC for nearly-true random numbers. Also, time and convenience do not matter in this case. Also also, I did use the web cryptography API combined with a lot of other CSPRNGs for entropy. Read below...

How it works

The "point" of this is to generate true random numbers without a hardware TRNG in the browser/site with JavaScript.

Of course, if an attacker can break a (CS)PRNG, they can break SSL. (TLS)

The mitigation would be to generate billions of bits of entropy locally, while mixing that with several "randomness beacons"

The (CS/T)PRNG works like this: generate as much local entropy, (into arrays), then get a bunch of numbers from randomness beacons, then hash them together. This is where things get baaaaadddd.

Problem

First, if the values aren't uniform, the values can be somewhat predictable.

Second, the hash function can make the random number lose entropy. A primitive caveman mitigation would be to just add a lot more entropy, but I want to fix this correctly.

So my solution for now is to take the hash (which is several thousand bits long; shake256 is used), re-hash it with sha512, and remove all the letters, and pass that massive number through a library which manages large integers, and then pass it through a uniformity function. This is a modified version of another function I found on SO here:

function uniform(SCKE) { //SCKE must be a string of a hash without a-z
    function uniform32 () {
        if (SCKE.length <= 23) {
             return SCKE;
        } else {
             return bigInt(SCKE).divide(10**(SCKE.length - 24)).toJSNumber();
        };
    }
    // sample e from geometric(1/2) by counting zeros in uniform bits
    // but stop if we have too many zeros to be sensible
    let e = 0, x
    while ((x = uniform32()) == 0) {
        if ((e += 32) >= 1075) {
            return 0
        }
    }
    // count the remaining leading zeros in x
    e += Math.clz32(x)

    // sample s' = sl + sh*2^32 from odd integers in (2^63, 2^64)
    // (beware javascript signedness)
    let sl = (uniform32() | 0x00000001) >>> 0
    let sh = (uniform32() | 0x80000000) >>> 0

    // round s' to floating-point number
    let s = sl + sh*Math.pow(2, 32)

    // scale into [1/2, 1]
    let u = s * Math.pow(2, -64)

    var srt = Math.trunc(u);
    return Math.abs(srt - (u * Math.pow(2, -e))); //MUST RETURN A FLOAT
};

Question(s)

  • That is my best mitigation against entropy loss and skewing, but it probably is skewed and has many holes. How can I fix this while keeping uniformity and entropy?
  • Is there anything else that could be a problem?

0 Answers0