I think most very experienced engineers would strongly disagree with you on (2) and would laugh at the usage characterization you make in (4).
On #2: The difference between a PRNG and a cryptographically secure random number generator is foundational knowledge for software engineers, and there's (finally) a strong trade consensus that writing security-sensitive code has a higher bar of preparation and shouldn't be done naively. It's entirely unreasonable that someone writing a `uuid` implementation that's meant to be a cryptographically secure library for use by application developers wouldn't anticipate that their standard library would offer both and would prioritize the PRNG as its basic and most accessible generator.
On #4: It's very rare that an application developer needs direct access to a cryptographically secure random number generator, especially because of that trade consensus that security-sensitive applications deserve expert care. In contrast, application developers are building test data pools, shuffling arrays, adding noise to signals, adding surprise to gameplay, writing and running reproducible tests, etc every day and naive use of a cryptographically secure random number generator in those contexts is easily paralyzing and catastrophic. The overhead can be order of magnitude in measure (which matters quite a lot in typical highly-repetitive/looped usage), and in some implementations it can introduce non-deterministic and potentially indefinite blocks on execution. And because they specifically can't be made deterministic by design, they significantly frustrate debug and test efforts that benefit from reproducibility. They're an essential tool in modern standard libraries, when they're needed in unusual cases, but are not suitable for naive everyday use at all.
I think the following statements are entirely compatible with one another:
* Most software developers (let's say, 95-99%+) are smart enough to avoid using insecure PRGs in their code.
* The remaining 1-5% have contributed to a litany of avoidable security disasters, nonetheless.
* The impact of these disasters has been vastly outsized when you compare it to the number of bad developers.
* If every developer understood that they needed security expertise (and indeed, that they were even writing security critical code, the world would be a very different place than it is.)
As Thomas mentioned below, I am something of a collector and obsessive when it comes to incidents of bad random number generation.
What's fascinating to me about this Dart/Flutter story is not the result, it's that I haven't seen one of these stories in a long time!
By this I mean: this type of incident used to pop up on HN every month or so, and yet in the past few years they've become incredibly rare. And do you know why that is? It is not because developers got better. It's almost entirely due to the fact that development frameworks (particularly JS in browsers) have made a multi-year and systematic effort to reduce the availability of insecure default PRGs to bad developers. The result is that an entire bug class has gone from a common, monthly occurrence, to a relative rarity.
The idea that we should use insecure default RNGs because they're good for reproducible testing is a new one on me. If you need reproducible random numbers for your application, but you are asking to use an interface that does not explicitly include the fact that it generates non-random, reproducible numbers then you are doing both software development and testing wrong, IMHO.
> this type of incident used to pop up on HN every month or so, and yet in the past few years they've become incredibly rare. And do you know why that is? It is not because developers got better. It's almost entirely due to the fact that development frameworks (particularly JS in browsers) have made a multi-year and systematic effort to reduce the availability of insecure default PRGs to bad developers. The result is that an entire bug class has gone from a common, monthly occurrence, to a relative rarity.
That's a fair take and worth considering.
I suspect this whole dilemma speaks to a widening divide between those who see security as the top and inviolate priority in modern software and those still seeing software as something with much more varied and heterogenous concerns. And that gap gets most contentious when it comes to what things like what should be considered default, what capabilities should be available altogether, how convenient those features should be made, what emphasis should be made in education/training/mentorship, etc.
Undoubtedly, (often sloppy) network-delivered and network-attached software has come to dominate the industry and with it the consequences of security-practice lapses have become be more severe than they once had been. But at the same time, we can watch as things like correctness, stability, resource-efficiency, performance-efficiency, maintenance/repair-ability, etc tumble away and turn much of the same software into byzantine garbage that barely runs on X-core YYY-ghz hardware with ZZ-gigabytes of RAM and faults have the time when it does.
So I think there's a real tension, but I get where you're coming from. It may in fact be true that making a seeded PRNG less easily available would be a wise favor to the security-first-and-always people. :D
Just to beat the horse to death, I want to be clear that what I'm asking for isn't much:
1. The default random() call/library should always produce exactly what it says -- real, secure, unpredictable (pseudo)random numbers.
2. For statistical and non-security applications it's perfectly fine to have a generator of the form random.insecureAndFast(). Or call it whatever you want, the important thing is that the developer make a tiny amount of conscious effort in the process of using it.
3. If you require reproducible and insecure generator, just use approach (2) above and add a seeding function. (Honestly I don't like anything that uses global state, but I don't care as long as it's labeled insecure.)
4. If you require reproducible and secure random numbers, then you have a slightly challenging problem on your hands that is way beyond the scope of this discussion.
For people who don't require secure randomness, the total "cost" of my proposal is something like an extra dozen characters added to your code in a few places -- and if that's too much work for you, then you probably aren't writing reproducible tests or spending a lot of time optimizing for speed. On the flipside, you might save someone a few million dollars when their crappy Bitcoin library uses a dependency that relies on random().
On #2: The difference between a PRNG and a cryptographically secure random number generator is foundational knowledge for software engineers, and there's (finally) a strong trade consensus that writing security-sensitive code has a higher bar of preparation and shouldn't be done naively. It's entirely unreasonable that someone writing a `uuid` implementation that's meant to be a cryptographically secure library for use by application developers wouldn't anticipate that their standard library would offer both and would prioritize the PRNG as its basic and most accessible generator.
On #4: It's very rare that an application developer needs direct access to a cryptographically secure random number generator, especially because of that trade consensus that security-sensitive applications deserve expert care. In contrast, application developers are building test data pools, shuffling arrays, adding noise to signals, adding surprise to gameplay, writing and running reproducible tests, etc every day and naive use of a cryptographically secure random number generator in those contexts is easily paralyzing and catastrophic. The overhead can be order of magnitude in measure (which matters quite a lot in typical highly-repetitive/looped usage), and in some implementations it can introduce non-deterministic and potentially indefinite blocks on execution. And because they specifically can't be made deterministic by design, they significantly frustrate debug and test efforts that benefit from reproducibility. They're an essential tool in modern standard libraries, when they're needed in unusual cases, but are not suitable for naive everyday use at all.