Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cryptographically Secure RNG #97

Open
benjaminBrownlee opened this issue Jun 29, 2017 · 17 comments
Open

Cryptographically Secure RNG #97

benjaminBrownlee opened this issue Jun 29, 2017 · 17 comments

Comments

@benjaminBrownlee
Copy link

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
Copy link
Owner

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.

@benjaminBrownlee
Copy link
Author

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
Copy link
Owner

peterolson commented Jul 4, 2017

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
Copy link
Author

benjaminBrownlee commented Jul 4, 2017

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
Copy link
Owner

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
Copy link
Author

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
Copy link
Author

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
Copy link
Author

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
Copy link
Owner

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
Copy link
Author

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
Copy link
Owner

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

@catb0t
Copy link

catb0t commented Jul 10, 2017

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

@benjaminBrownlee
Copy link
Author

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

@peterolson
Copy link
Owner

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

@benjaminBrownlee
Copy link
Author

benjaminBrownlee commented Aug 19, 2017

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
Copy link

Uzlopak commented May 23, 2020

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

@Rudxain
Copy link

Rudxain commented Jun 13, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants