0
$\begingroup$

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?
$\endgroup$
  • $\begingroup$ Are you aware that the code you copied & pasted without credit assumes, as a premise, that the outcomes of uniform32() are uniformly distributed, and that it returns the bits of two calls to uniform32() nearly verbatim as the significand of $u$? What are you trying to accomplish with this construction that SHAKE256 doesn't already do—what do you expect to come out of using this code instead of just returning the SHAKE256 output directly? (And what is removing letters and passing it through a bignum supposed to do?) $\endgroup$ – Squeamish Ossifrage Aug 30 at 2:15
  • $\begingroup$ I used the function to achieve a uniform approach since the SHAKE value was too big to work with in JS. If I had a 1000+ digit number string, that would be useless, so I cut it down with bigInt and then applied the uniform function. $\endgroup$ – Display Name Aug 30 at 2:17
  • $\begingroup$ Also, I will go credit the source (gonna go find the author and edit the post) $\endgroup$ – Display Name Aug 30 at 2:19
  • $\begingroup$ Are you just trying to make the original uniform_01 code work reproducibly with a seed, to sample floating-point numbers in [0,1] uniformly at random and reproducibly? There's a simpler way to do that—pass in a seed and change uniform32 to call a generic CSPRNG instead of window.getRandomValues. (But I don't understand what the $\left|\mathit{srt} - u\cdot 2^{-e}\right|$ business at the end is supposed to do; the result is certainly not the uniform distribution on [0,1].) $\endgroup$ – Squeamish Ossifrage Aug 30 at 2:19
  • $\begingroup$ Isn't that what I did with the SCKE variable? The original SCKE input would be uniform(shake256("txt", 1000).replace(/([a-z])/gm, "").toString()) $\endgroup$ – Display Name Aug 30 at 2:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.