BigInteger.js icon indicating copy to clipboard operation
BigInteger.js copied to clipboard

Cryptographically Secure RNG

Open benjaminBrownlee opened this issue 7 years ago • 17 comments

This is more of a recommendation than an issue, but the bigInt.randBetween() function could be enhanced with a cryptographically secure alternative. Most of the content can be copied, but instances of Math.random() can be replaced with a function that returns window.crypto.getRandomValues(new Uint32Array(1))[0]/4294967295. For performance conditions, the original should be kept, but this new option would definitely be useful.

benjaminBrownlee avatar Jun 29 '17 22:06 benjaminBrownlee

I'll have to think about this some. In principle I like the idea, but there are a few kinks to consider:

  • We'll have to fall back to Math.random for browsers that don't support crypto (although nowadays that is relatively a fairly small percentage of users)
  • BigInteger.js is used for both browser and node.js platforms. In node.js there is also a built-in crypto.randomBytes method with an API slightly different from window.crypto.getRandomValues. We would have to detect which methods are supported in the environment that BigInteger is run in.
  • As you mentioned, performance is something to consider, although I'm unaware of anybody using bigInt.randBetween for tight-performance applications. Anyway, we'd have to do some profiling to see what the consequences would be.

peterolson avatar Jul 03 '17 14:07 peterolson

Thanks for your consideration on this topic. I recognized by your earlier comments on past issues that you are hesitant in gearing your library towards cryptography, but I can assure that it does assume a healthy portion of you audience. I will try to do some further research on this. I must admit I oversaw cross-compatibility when I proposed my solution, as it was a function I personally added to the library when developing my own RSA program for Chrome. Two questions that might lead to solutions:

  1. Can your library detect the platform its running on (node vs browser) and address them differently? (Complicates library, user friendly)
  2. Could the bigInt.randBetween() function have an optional argument to pass in that could personally define the function used as a RNG? (Easy fix, but requires more work from user)

benjaminBrownlee avatar Jul 04 '17 04:07 benjaminBrownlee

I'm inclined to implement this since this is not the first time this has been requested.

This will be straightforward if I have a function random(max) that returns a random number in the range [0, max), with max being at most 9007199254740992. With Math.random() the best we can do is

Math.floor(Math.random() * max)

With window.crypto.getRandomValues we could try

Math.floor(window.crypto.getRandomValues(new Uint32Array(1))[0] * max / 4294967295)

This is a quick-and-dirty way that kind of works, but it will not generate a uniform distribution, and will skip some values if max is significantly larger than 4294967295.

The same thing is true for crypto.randomBytes in node.js.

If you could look into an elegant way to do this with window.crypto.getRandomValues and crypto.randomBytes, that would be helpful.

peterolson avatar Jul 04 '17 17:07 peterolson

I have found a method to generate a random number up to any specified number of bytes, which could be used as a more accurate multiplier that you were mentioning above: function random(num_bytes) { var random_array = window.crypto.getRandomValues(new Uint32Array(num_bytes)); var big_random = ""; for(var i = 0; i < num_bytes; i++) { var small_random = bigInt(random_array[i]).toString(2); while(small_random.length < 32) { small_random = "0" + small_random; } big_random += small_random; } return bigInt(big_random, 2); }

benjaminBrownlee avatar Jul 04 '17 20:07 benjaminBrownlee

If we use a number of bytes, it only works for the situation when the size of the range of numbers we want is a power of 256. How do we find a random number between 0 and 1000, for example?

peterolson avatar Jul 04 '17 20:07 peterolson

So first we identify an upper and lower bound to compute a range. Then we multiply the range by x amount of 32-bit bytes and divide by 2 to the power of the number of bits minus one. Finally, add the lower bound. So, using the function random() defined previously: function crypto_randomBetween(a, b) { var min = bigInt.min(a, b), max = bigInt.max(a, b); var range = max.subtract(min); var num_bytes; return min.add(range.multiply(random(num_bytes)).divide(bigInt[2].pow(num_bytes * 32).prev())); } Obviously, the number of bytes used for the multiplier must depend on the magnitude of the range, but I have yet to finalize that math.

benjaminBrownlee avatar Jul 04 '17 20:07 benjaminBrownlee

Note I did make a small edit to the random bytes generator above as leading zeros were being lost in the process of decimal to binary conversion.

benjaminBrownlee avatar Jul 05 '17 02:07 benjaminBrownlee

I have done a bit of work and would like to submit a final RNG for you to test and approve. I now believe it to be as random as the original constructive function, Math.crypto.getRandomValues(), but I am not sure of a better method to submit a larger block of code. Where could I do this?

benjaminBrownlee avatar Jul 10 '17 16:07 benjaminBrownlee

If you're wanting to put it directly in the library you can make a pull request. If you just want to share some code with me you can make a gist and I'll look at it

peterolson avatar Jul 10 '17 16:07 peterolson

Thank you, I am relatively new to GitHub and am still learning all its processes. Here is my RNG: https://gist.github.com/benjaminBrownlee/2823e06d0b455969a06a13eddbadeb48 It appears to be working, but I imagine you have the skill/resources to better test its accuracy and distribution.

benjaminBrownlee avatar Jul 10 '17 16:07 benjaminBrownlee

OK, thanks, I'll try to look at it later

peterolson avatar Jul 10 '17 16:07 peterolson

Clicking that link appears to be broken, here's the intended target: https://gist.github.com/benjaminBrownlee/2823e06d0b455969a06a13eddbadeb48

catb0t avatar Jul 10 '17 18:07 catb0t

Thanks for fixing the URL. As I said, I am still getting used to GitHub.

benjaminBrownlee avatar Jul 23 '17 19:07 benjaminBrownlee

Sorry I still haven't tried adding this to the library, I've been rather busy lately

peterolson avatar Jul 23 '17 19:07 peterolson

Is anyone aware of where to find tools or processes to check mass calculations? I want to run my proposed RNG at numerous bit sizes and collect the data to check for accuracy and distribution.

benjaminBrownlee avatar Aug 19 '17 18:08 benjaminBrownlee

How about injecting the random method instead of having so much headaches about it?

Uzlopak avatar May 23 '20 08:05 Uzlopak

@benjaminBrownlee you can format your code blocks by using triple backticks, like this: ```js //JavaScript code here ```

Adding the language name (I used the short version "js" but it also works with "javascript") activates syntax highlighting for that lang (if supported)

Rudxain avatar Jun 13 '22 16:06 Rudxain