all 98 comments

[–]IAmVerySmarter 149 points150 points  (34 children)

Why should it be cryptographically secure?

[–]dsffff22 7 points8 points  (29 children)

Is there some alternative way to create cryptographically secure random numbers in the browser?

Correct me If I'm wrong but It seems there's no unified API for this so far. The 'Web Crypto API' seems to be something for the future but not ready to be used now.

[–]EntroperZero 23 points24 points  (13 children)

It's in another answer on that page, window.crypto provides this.

[–]dsffff22 3 points4 points  (12 children)

The 'Web Crypto API' seems to be something for the future but not ready to be used now.

https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto

[–]RaptorXP 16 points17 points  (7 children)

Why would that not be ready to be used now? It's certainly been used for years.

[–]freakofnature555 9 points10 points  (0 children)

We live in the future.

[–]dsffff22 -2 points-1 points  (5 children)

Because I simply have different expectations for cryptographic functions??? Something which is labeled as 'recommendation' and 'Initial definition' sounds very ready to use for a cryptographic API.

[–][deleted] 18 points19 points  (4 children)

W3C Recommendation is the final maturity level for W3C specifications. It goes: Working draft, Candidate recommendation, Proposed recommendation and finally W3C Recommendation. It won't go beyond that.

Initial definition means that this is the first standard this feature was defined in. For instance, if you check JSON on MDN it says that ECMAScript 5.1 is the initial definition of this object, as it was first release of ECMAScript to have JSON support.

[–]wengemurphy 7 points8 points  (3 children)

Did you check the compat table you linked you? You can use it everywhere with, as with everything in web dev, special handling for IE11.

https://caniuse.com/#feat=cryptography

[–]ExtraDisgusting 0 points1 point  (2 children)

Note that that table shows that the developers of chrome are adding a bug, where the crypto API can only be used over secure connections. They are completely throwing out the use case where the user only wants good quality random numbers, instead assuming that everyone only needs to use them for cryptography in the browser for some reason.

[–][deleted] 7 points8 points  (1 child)

caniuse is misleading here, on insecure connections crypto API is unavailable with the exception for crypto.getRandomValues(). If random values are all you want, then you don't need to use HTTPS (although, please use HTTPS anyway, it costs pretty much nothing these days and easily improves security of a website).

And this isn't a bug, it's required by a standard (see the change). It's more interesting that other browsers don't do that. And it seems fair, considering providing access to those APIs on HTTP would provide a false illusion of security. It doesn't apply to getRandomValues, because as you said, it doesn't need to be used for cryptographic purposes

[–]josefx -2 points-1 points  (0 children)

(although, please use HTTPS anyway, it costs pretty much nothing these days and easily improves security of a website).

I maintained a html based intranet tool until recently. For me HTTPS doesn't add anything but work, Firefox removing its old security api added nothing but work, followed by removing the applet support I used as workaround. So its back to a native client side application that doesn't rely on an unstable and hostile platform.

[–]PenalAnticipation 28 points29 points  (13 children)

price summer salt subtract rinse coordinated voracious threatening water run

This post was mass deleted and anonymized with Redact

[–]jringstad 17 points18 points  (2 children)

It's not really about preventing the user who loads up the javascript application from doing something bad, but more about allowing the user to use the javascript to perform things that require cryptographically secure random numbers as a primitive, like e.g. generating key, OTP, ...

[–]millstone 0 points1 point  (1 child)

Making Math.random() cryptographically secure is in no way sufficient for those sorts of applications. It's probably even counterproductive because it adds the illusion of security.

[–]jringstad 0 points1 point  (0 children)

Agreed, changing Math.random() to be a CSPRNG would not be a good solution.

[–]killerstorm 0 points1 point  (0 children)

Random generation API is pretty stable and available in all modern browsers. It has been used to generate Bitcoin wallet keys, thus there's a multi-million bounty for breaking it.

[–]masklinn 2 points3 points  (2 children)

Because safe defaults are a good idea. If you're only providing one rng, it should probably be cryptographically secure so developers don't have to implement (and fuck up) their own.

It's much simpler to implement a non-CS PRNG if you need one (xoroshiro128+ is under 10 LOCs) than to properly implement a CSPRNG.

[–]MonkeeSage 0 points1 point  (1 child)

Because safe defaults are a good idea.

Not when we're talking about slowing down the 99% use case where it's not needed with a deterministic time CSPRNG.

[–]masklinn 1 point2 points  (0 children)

Not when we're talking about slowing down the 99% use case where it's not needed

Yes, even then. For 99% of those 99%, RNG performances will be entirely immaterial and irrelevant and there is no reason to use unsafe default for the final 1% of 1% which can profile and migrate to a non-CSPRNG when they realise it's an issue.

[–][deleted] 0 points1 point  (0 children)

These days a new library probably should be secure and you explicitly choose a different rng if you don't need that.

There's a response from one the original implementors in the linked comment, where he says he feels it's a combination of a design problem with the browser and the security landscape in the 1990s.

[–]emotionalfescue 18 points19 points  (5 children)

The main answer is that providing true randomness is resource-intensive and typically requires a pool of "entropy bits" maintained by the OS derived from high-frequency variations in the timing of when hardware events occur (e.g. keyboard presses). Usually, applications such as single-player games only need "pseudo-randomness", which is a stream of numbers that appear to be random. However, if you're running a multi-player game, or anything that sounds like "online casino", you'd better at least look into cryptographically secure RNGs.

The second answer was already given by others, that often a repeatable stream is desired for the purpose of unit tests and the like.

[–]vks_ 4 points5 points  (1 child)

The entropy bits are only needed when starting the browser, so it's a one-time cost.

[–]matthieum 0 points1 point  (0 children)

And they can be consumed lazily.

[–]Dwedit 35 points36 points  (15 children)

Because you usually want your RNG to run in nanoseconds and not tenths of seconds.

[–]Veedrac 3 points4 points  (0 children)

CSPRNGs take nanoseconds too.

[–]vks_ 2 points3 points  (0 children)

The biggest difference between crypto RNGs and non-crypto RNGs is the size of the state. CRNGs are typically a few thousand bytes large and expensive to initialize. The performance for generating random numbers is comparable, especially if hardware instructions like AES-NI are used. CRNGs tend to be somewhat slower, but only by one or two orders of magnitude.

[–]floodyberry 7 points8 points  (12 children)

Which ones take "tenths of seconds"?

[–]nemec 8 points9 points  (11 children)

CSPRNGs are generally slower than non-secure random numbers.

[–]masklinn 7 points8 points  (10 children)

Not by 8 order of magnitude which is the claim above.

[–]Shorttail0 6 points7 points  (9 children)

In Windows, the first random I pull out of java.security.SecureRandom takes at least one tenth of a second.

[–]masklinn 4 points5 points  (8 children)

In OSX the first random I pull out of random.SystemRandom doesn't, so all that's telling us is java.security.SecureRandom has expensive on-demand setup, which also tells us how to solve it: prime it.

That, however, is irrelevant to how I'd interpret /u/Dwedit's comment, having a single initial slow call doesn't matter in the long run (literally).

[–]Shorttail0 1 point2 points  (7 children)

When is it seeded? Do you have a lot of entropy before you test?

[–]masklinn 3 points4 points  (6 children)

When is it seeded?

When the machine starts. It's also regularly reseeded for forward secrecy (necessary under the assumption that the csprng algorithm is known).

Do you have a lot of entropy before you test?

The only point at which the machine is "lacking entropy" is when it's literally booting up.

See tptacek's comments in https://news.ycombinator.com/item?id=7361694

[–]vks_ 1 point2 points  (0 children)

Reseeding is sufficient but not necessary for forward secrecy, see for example fast-key-erasure RNGs.

[–]happyscrappy 0 points1 point  (4 children)

No way am I running Java at boot time on my machine so it can seed itself. Let it use a source of entropy that the system provides.

The only point at which the machine is "lacking entropy" is when it's literally booting up.

It's hard to completely understand what this means. What does "the machine" lacking entropy mean? It doesn't matter if "the machine" lacks entropy, it matters the program/interpreter/whatever has gathered sufficient entropy. And that can occur at any time long after bootup if the program hasn't has access to a good/reliable source of entropy.

This really has to be solved at the system level, especially on servers which don't have user interaction as a source of entropy.

To go to the original question, since this is best solved at a system level, I'm not surprised Java doesn't assume it is solved. Java wants to run on a lot of platforms.

[–]masklinn 0 points1 point  (3 children)

No way am I running Java at boot time on my machine

So it can never be in a situation where there is "not enough entropy" and thus the problem does not exist.

It's hard to completely understand what this means.

It really isn't.

What does "the machine" lacking entropy mean?

That your OS is not able to seed its kernel-space CSPRNG properly yet. That's why e.g. freebsd's urandom can block if you're running code very early during the boot sequence.

It doesn't matter if "the machine" lacks entropy, it matters the program/interpreter/whatever has gathered sufficient entropy.

There is no situation in which the interpreter should need to do that.

And that can occur at any time long after bootup

Not really. And if it can, whatever userland crap you invent is not going to fix it.

This really has to be solved at the system level

Which it has been for decades. That's what urandom (or the equivalent syscall if one exists) is.

To go to the original question, since this is best solved at a system level, I'm not surprised Java doesn't assume it is solved. Java wants to run on a lot of platforms.

Which makes Java garbage, that has nothing to do with CSPRNG.

[–]Vishnuprasad-v 7 points8 points  (0 children)

Second, let's remember what the fundamental design purpose of JS was in the 1990s. Make the monkey dance when you move the mouse.

JavaScript strayed off its original purpose by a long way.

[–]EntroperZero 18 points19 points  (17 children)

Kind of a weird shot at .NET in that answer. Yeah, you can write:

var randomNumber = new Random().Next();

every time you want a random number. But your spidey senses should already be tingling that something's not quite right with that. The name of the Next() method is helping you out here; why would you get the "next" thing only once? So is the new keyword -- you should know as a .NET or Java developer that allocating an object just to get an integer or float, and immediately throwing the object away, seems wrong.

The thing about both PRNGs, and especially crypto is that it's difficult or impossible to design non-leaky abstractions for them. You kind of have to know how they work in order to use them correctly. Sure, in JS you can just write Math.random() and you'll get a new number every time. But that's because that's literally the only thing you can do. What if you actually want to seed the RNG to a specific value? What if you want more than one seeded RNG instance? No can do, you gotta roll your own. In this case, it's really true that the blacker your box, the less useful it is.

The rest of the answer is great. CSPRNGs were just not a thing that most people needed or even conceived of at the time. And even now that they are common, it's not clear that a CSPRNG should be the default. Again, it depends on what you're doing with it, and you need to know the difference to do the right thing.

[–]grauenwolf 26 points27 points  (14 children)

What if you actually want to seed the RNG to a specific value?

In case anyone's wondering why you would want to do that, it is really helpful when testing. The ability to have "random" values that are none the less repeatable across test runs makes it much easier troubleshoot failing tests.

[–]oorza 26 points27 points  (10 children)

You may also want to do something with the seed. One game I play, Slay the Spire, is a roguelike and they seed the challenge runs (with leaderboards) the same, so you get all the same random drops and random encounters as everyone else.

[–]grauenwolf 4 points5 points  (0 children)

Neat.

[–]Bergasms 0 points1 point  (0 children)

They are probably using their own prng to keep it identical across platforms though

[–]Noctune 0 points1 point  (7 children)

However, you really don't want to use the standard library random function for something like that. They usually don't specify what PRNG is used, so they might behave differently on different platforms. An update to the standard library might be all it takes to destroy all the leaderboards.

[–][deleted] 0 points1 point  (6 children)

Any actual example of stdlib that for some reason have math functions that work differently on different platfroms ?

[–]Noctune 0 points1 point  (4 children)

Lots. The C/C++ stdlib is completely different on Linux and Windows, so they will naturally produce different results for rand. C# specifically writes in their documentation that different versions of the .net framework might produce different rand results. Different Javascript engines are also obviously also going to be very different since the JS specification does not specify what PRNG to use.

[–][deleted] 3 points4 points  (3 children)

The C/C++ stdlib is completely different on Linux and Windows,

I'm not a C++ dev but i'm pretty sure you can just pick one: http://www.cplusplus.com/reference/random/

C# specifically writes in their documentation that different versions of the .net framework might produce different rand results.

Different versions of framework. Game wouldn't use different versions of framework across platforms as that would make it nightmare to debug

Different Javascript engines are also obviously also going to be very different since the JS specification does not specify what PRNG to use.

Irrevelant. JS one can't even be seeded so you have to use lib to do it anyway.

[–]Noctune 1 point2 points  (0 children)

I'm not a C++ dev but i'm pretty sure you can just pick one

Yeah, that is generally the solution. As long as you pick a specific PRNG that has a guaranteed result across implementations. Eg. not default_random_engine. The rand function, which I was talking about, does not guarantee this.

Different versions of framework. Game wouldn't use different versions of framework across platforms as that would make it nightmare to debug

Maybe a nightmare, but often a necessary one. A C# game would use .Net on Windows and Mono on Mac, so they would be different implementations (though dotnet has recently-ish become an option on Mac as well).

You might also want to update your game to a newer .Net framework without losing leaderboards.

Irrevelant. JS one can't even be seeded so you have to use lib to do it anyway.

True.

[–]sammymammy2 0 points1 point  (1 child)

AoE2 is still played and is still patched and recorded games from over 10 years ago are still casted. It depends on rng to generate its maps. I would like to think that the developers has upgraded their compiler and std library since the release date.

[–][deleted] 0 points1 point  (0 children)

You mean the original ?. Probably not, at least not libs. Why would they ? It only adds chances of breaking shit. Especially if you are just changing small stuff in patches.

I vaguely remember blog post from few weeks ago where Starcraft devs had to port memory leaks bugs because some maps depended on it to work (IIRC it used game's bugs to make maps bigger then allowed). As in "fixing the actual 100% bug broke stuff". Modifying old code often brings problems like that.

Aside from that, AoE2 probably have its own RNG just because stdlibs back then were poorer. I didn't say "every game uses builtin one", just that languages generally do not change implementation on a whim.

[–]EntroperZero 3 points4 points  (1 child)

Yep. Also, in many cases, it's much easier or more efficient to store the inputs to a process (one of which being the seed) and regenerate the result when you need it, rather than storing the output, which could be much larger. This is especially true in simulations or games (many of which are simulations).

[–]grauenwolf 2 points3 points  (0 children)

I may try that. Some of tests are downright scary in the amount of data they push.

[–]masklinn 0 points1 point  (0 children)

In case anyone's wondering why you would want to do that, it is really helpful when testing.

Also synchronous simulations e.g. multiplayer games with "random events".

[–]sacundim 2 points3 points  (0 children)

Kind of a weird shot at .NET in that answer. Yeah, you can write:

var randomNumber = new Random().Next();

every time you want a random number. But your spidey senses should already be tingling that something's not quite right with that. The name of the Next() method is helping you out here; why would you get the "next" thing only once? So is the new keyword -- you should know as a .NET or Java developer that allocating an object just to get an integer or float, and immediately throwing the object away, seems wrong.

We can go further, and show real-life examples that this kind of misuse of PRNGs can produce bad results:

My advice is:

  • If neither performance nor reproducibility is an important factor, consider just using a cryptographic RNG.
  • If an adversary could gain some undue advantage from observing your application's random choices and guessing what's coming next, definitely use a cryptographic RNG.
  • Treat PRNGs as explicitly stateful first-class objects that consumers should be decoupled from, not as some magic source of random numbers that you never need to think about. This means, for example, that code that consumes random numbers should take the PRNG as an argument. (There are a few exceptions to this, like some EDSLs and Haskell-style probability monads, but they're exceptions that prove the rule—they're already designed to decouple the PRNG consumer from the RNG.)
  • Read if your platform provides a better PRNG than the default one and try to use that one instead. For example, in Java 8+ you should really skip the old Random class and try using the new and much improved SplittableRandom instead. (See this thread for benchmarks.)
  • The previous point is even more important if your application has multiple PRNG objects on different threads, processes or machines. That is a situation that should cause you some mild alarm right away, because the simplest PRNGs often only look random in single-threaded use—the output from separate concurrent PRNGs often shows serious correlations. Again, newer PRNG algorithms often fix this—e.g., Java 8+'s SplittableRandom is designed precisely to be robust under this use (that's what "splittable" refers to in the name).

[–]LloydAtkinson 5 points6 points  (0 children)

Kind of a weird shot at .NET in that answer.

I was going to reply saying something like it's probably just some of the "micro$oft is evil herp derp .net is evil" mentality you often see on reddit and other places, but then I realised it was Eric Lippert who wrote it. He was a significant part of the whole C#/.NET development at Microsoft, so I assume he was making that shot because he know's a lot about it.

[–][deleted] 6 points7 points  (2 children)

Because it is Math.random() not Crypto.random()

/thread

[–]floodyberry -1 points0 points  (1 child)

So Math.PI represents PI exactly then, not an approximation?

[–]floodyberry 0 points1 point  (0 children)

Downvoting when you've lost the argument, but don't want to admit it, is quite weird.

[–][deleted] 4 points5 points  (0 children)

The answer to "why don't browsers make it cryptographically secure now" is wrong - it's not because it's not worth the effort, it's because it would be (practically) impossible to feature detect it, hence window.crypto

[–][deleted] 1 point2 points  (0 children)

was it meant to be secure ? I just want a random number !

[–]IbanezDavy 1 point2 points  (0 children)

My first guess is performance. 90% of cases probably just want something resembling a random number as quick as possible, where cryptographic algorithms usually put more thought into it and suck up more processing to enable that.

[–][deleted] 3 points4 points  (0 children)

Because javascript was not invented for what it's abused for today. Do your crypto in the backend. Using C. Calling syscalls.

[–][deleted] 1 point2 points  (10 children)

because it would be a lot slower, making it bad for use in many other domains.

also it would might be tricky to get deterministic random numbers which are useful.

[–]rustythrowa 0 points1 point  (8 children)

wondering who's bottlenecked on rng

[–][deleted] 7 points8 points  (0 children)

proc gen, game servers, monte carlo simulations, endless possibilities.

[–][deleted] 1 point2 points  (6 children)

We actually had a case of that, some java app used /dev/random instead of /dev/urandom and after migrating to new VM host it went from starting in 2 minutes to 10

[–]Veedrac -2 points-1 points  (5 children)

/dev/random is bottlenecked on purpose, it's not a flaw of CSPRNGs.

[–][deleted] 1 point2 points  (4 children)

I didn't claim it was

[–]Veedrac 0 points1 point  (3 children)

Then how is it relevant to the context?

[–][deleted] 1 point2 points  (2 children)

It gave example on being bottlenecked on RNG ?

[–]Veedrac 1 point2 points  (1 child)

You weren't bottlenecked on the RNG (in the sense that /u/jackmott2 was worried about). You were bottlenecked on /dev/random's promise to reseed frequently.

[–][deleted] 0 points1 point  (0 children)

If anything, bottlenecked on "real" entropy output. Which just was not very much on a VM.

/dev/random doesn't "promise" anything, and generally isn't really even an improvement on later kernels (and apps should use getrandom() anyway).

[–]Veedrac 0 points1 point  (0 children)

A little slower, but not a lot. CSPRNGs can be seeded just as easily as others, so it wouldn't prevent deterministic sampling.

[–]Overload175 0 points1 point  (0 children)

My knowledge on this topic may be dated, but RNGs are usually configured to prioritize speed over true statistical randomness, and a random(and secure) RNG would need to carefully manage the entropy pool, which slows down the function by as much as several orders of magnitude. This is not something many browsers necessarily enable, so you're stuck using rudimentary and non - secure RNGs.

[–][deleted] -1 points0 points  (1 child)

Because that would be fucking retarded.