peterolson / BigInteger.js

An arbitrary length integer library for Javascript
The Unlicense
1.12k stars 187 forks source link

Cryptographically Secure RNG #97

Open benjaminBrownlee opened 7 years ago

benjaminBrownlee commented 7 years ago

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.

peterolson commented 7 years ago

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

benjaminBrownlee commented 7 years ago

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)
peterolson commented 7 years ago

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.

benjaminBrownlee commented 7 years ago

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); }

peterolson commented 7 years ago

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?

benjaminBrownlee commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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?

peterolson commented 7 years ago

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

benjaminBrownlee commented 7 years ago

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.

peterolson commented 7 years ago

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

catb0t commented 7 years ago

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

benjaminBrownlee commented 7 years ago

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

peterolson commented 7 years ago

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

benjaminBrownlee commented 7 years ago

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.

Uzlopak commented 4 years ago

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

Rudxain commented 2 years ago

@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)