Use system random generator in crypto #5798
Comments
mscdex
added crypto feature request
labels
Mar 19, 2016
|
Instead of linking to a blog post, please summarize the pros and cons here. |
|
Perhaps @joepie91 or @paragonie-scott would be interested to elaborate that here. |
paragonie-scott
commented
Mar 19, 2016
|
From "a blog post":
Additionally, OpenSSL's userspace PRNG has caused issues in other languages (PHP under Apache for sure) because it's not fork-safe, which caused random numbers to repeat. (Combine this with the consequences of nonce reuse for most stream ciphers and boom your cryptosystem loses all privacy.) A safer bet would be to adopt what PHP 7's |
|
OpenSSL's PRNG is seeded from /dev/{,s,u}random, can get entropy from entropy-collecting daemons, and forking is not an issue for node.js. So far I'm not seeing a compelling reason to switch. |
|
@paragonie-scott That's does not look like a good reasoning. Could you please be more calm? ;-) |
|
@bnoordhuis Trying to find specific reasons to explain away potential issues is not a good approach to security-related matters. The issues exist, and there's no way to predict how Node will evolve - for example, if fork-safety might start to matter due to future changes, at which point people will likely have forgotten about this specific issue. We should be striving for the optimally secure implementation (within technical constraints), instead of attempting to 'defend' the current implementation when there are known edge cases / issues with it. This post addresses that further. |
|
I do not yet have a strong opinion on this. I called @joepie91 and @paragonie-scott here because they expressed similar considerations as @speakeasypuncture in an IRC chat earlier. @bnoordhuis, as I understand their points, the reasons here are:
Everyone — did I miss anything? |
ChALkeR
added the
security
label
Mar 19, 2016
|
Again: I do not yet have a strong opinion on this. I would like to hear what would the drawbacks of this change be, and is there something where (or why) OpenSSL PRNG could be better than the system PRNG. |
|
/cc @nodejs/crypto. |
|
@mscdex I'm not sure if this is a «feature request», it looks like a proposal to change the implementation of |
|
@ChALkeR Your summary seems accurate to me. |
paragonie-scott
commented
Mar 19, 2016
|
Also, random number generators are hot-swappable without compatibility concerns, only security concerns. |
|
I'm -1 on this, no compelling reasons for me. |
|
@indutny Please refer back to this comment in particular, plus several others in the thread. There is a lack of reasons not to do this (insofar anybody has stated them, that is), while there are documented reasons to do it. If you feel that there is a reason not to do it, then please share it - but "no compelling reasons for me" really isn't a sufficient argument for a security-related matter. Even a small defect can have disastrous consequences. |
|
@joepie91 sure, sorry for too short reply. What about systems with "good" PRNG? How many of them are there? Do we have to carry both implementations to support them? |
|
@indutny Thanks for the elaboration. As I understand it (and please correct me if not), OpenSSL depends on the system PRNG to begin with. I'm not aware of any platforms (of those supported by Node, that is) where OpenSSL can provide a better PRNG than the one that the system offers natively. It should thus be possible to just remove OpenSSL's PRNG from the equation entirely, and rely purely on the system PRNG, as PHP has done. |
|
@joepie91 do you suggest to use this randomness for TLS protocol as well? I'm not sure if it is possible, though. |
|
While that would probably be nice (albeit requiring more investigation), I don't think that's doable. As far as I know, OpenSSL's other functionality relies internally on its own PRNG with no ability to change that - unless we want to get rid of OpenSSL entirely, which would be a separate proposal (and likely not viable at this stage, given the lack of well-tested alternatives). So, this specific proposal would concern the "user-facing" |
|
Ok, considering arguments it probably make more sense now. |
|
Here's a reason not to switch: OpenSSL's PRNG is a known quantity, the strength of platform-specific PRNGs is not. If the next Windows or FreeBSD release has a flawed PRNG, that will compromise node.js. If (generic) you think moving to platform-specific solutions is the way forward, get OpenSSL to adopt your approach and node.js will automatically go along. I believe @paragonie-scott is volunteering? He sure seems to feel strongly about it.
@joepie91 The bucket list of fork-safety issues that would have to be addressed is so long that I think it's safe to say that node.js will never be fork-safe. There are many things that keep me awake at night but this is not one of them. |
alfiepates
commented
Mar 20, 2016
I don't believe this argument holds any merit, honestly.
I'm going to be frank, here: Attempting to justify away security issues is downright irresponsible, and possibly dangerous. You need to strive for bulletproof security, or you may as well not implement any security at all (And no, don't twist this to mean "Don't bother with security", because that's just lazy.) I appreciate it's "extra work" to make a change like this, but considering you're using a PRNG which has actually caused security issues in the past, I'd err on the safe side and move to something more provably secure. |
Besides being an appeal to authority, you're asking (generic) me to trust several teams of implementors (the platforms) instead of just one (OpenSSL) to get their implementation right.
Are you saying you feel OpenSSL's PRNG is insecure? If so, why aren't you taking that up with the OpenSSL team? Griping about it here isn't going to do any good. I'll repeat what I've said above: get the upstream project to move over, and we as downstream consumers will automatically move along with it. |
Yet OpenSSL's PRNG relies on these platform-specific PRNGs, and mixing in one broken PRNG can weaken the entire (combined) PRNG. How does relying on OpenSSL fix the issue you've described?
That is a very dangerous assumption to make.
I think the existence of this project should give an indication. At this stage, there's a fairly wide consensus around the security community that OpenSSL is awful software, and the only real reason it is still being recommended is because it's what has been tested in the real world for so long. Not because it is of high quality or well-maintained. |
ChALkeR
added the
discuss
label
Mar 20, 2016
paragonie-scott
commented
Mar 20, 2016
This is a common argument that people make, but it's ultimately invalid. Even if you avoid depending on the operating system's PRNG, the rest of your system definitely depends on it for security. Node.js will be compromised regardless of what Node.js does. |
|
Just a bit of FYI for everyone here: http://lwn.net/Articles/633805/rss "FreeBSD random number generator broken for last 4 months" |
|
@indutny Note that it also affects keys generated by Node.js on FreeBSD (using
|
ircmaxell
commented
Mar 20, 2016
Actually, if Windows's CSPRNG is compromised, then Node.JS will be compromised. This is because OpenSSL relies on the system CSPRNG to seed it. There is no other source of high-quality random data in the system. This means that the OpenSSL CSPRNG can by definition not be any stronger than the system's CSPRNG. However, it can be weaker (as has been seen several times). The reason @paragonie-scott and others (including myself) are anti-userspace-csprng is that they provide no possible security gain, but introduce several security risks (by increasing attack surface area, by increasing bug surface area, etc). There really is no benefit to not switching (other than not making a change at all). However, as has been demonstrated in this thread already, there are several advantages to switching. My suggestion would be to switch to the kernel-space CSPRNG. |
|
@ChALkeR this was a reply to:
|
|
@indutny yes, this assumption by @alfiepates is incorrect:
Everyone makes mistakes, you can't say «{*} are not just going to push broken code» or «this lib is magical, shining, and will never be broken». But what should be actually considered here is the fact that OpenSSL PRNG depends on system PRNG. So, it seems like under no circuimstances you could trust OpenSSL PRNG more than system PRNG, which makes «we can't trust system PRNGs, so let's use OpenSSL» argument invalid. |
alfiepates
commented
Mar 20, 2016
|
I will retract some of my comment, it was far too absolute, but I don't believe it's wrong. It's disingenuous to link this article without stating the fact that this occured on the FreeBSD -CURRENT branch, as opposed to the -STABLE branch. The -CURRENT branch is the bleeding-edge branch, and therefore bugs do happen, and in this case the bug was caught before the it made it to -STABLE.
|
|
@ChALkeR of course, I'm not saying that this is a counter-argument. Just wanted to make sure that everyone on the same page and does not trust OS vendors blindly. |
|
@alfiepates hey, I'm not missing the point here! |
alfiepates
commented
Mar 20, 2016
|
@indutny Ahh, wonderful. I'll retract that part of my comment too :P (Please forgive me, I'm one cup of coffee behind right now) |
|
There is however a risk of incorrect implementation on our side, when we will write that new code to support OS-level PRNG. Just some food for thoughts. |
|
@indutny True. There is also a blocking-vs-nonblocking argument that would need to be discussed when implementing this (i.e. |
paragonie-scott
commented
Mar 20, 2016
|
@indutny Sure. I'm almost certain that the rough draft will be incorrect in some way. That's why peer review matters. For what it's worth, I did help with PHP's implementation and I work for a company that audits crypto code. There are others in this thread that are often more perceptive than I am. So as long as we participated it's highly likely that any implementation bugs will be spotted and rectified. |
paragonie-scott
commented
Mar 20, 2016
|
@ChALkeR: https://blog.cr.yp.to/20140205-entropy.html
Also http://www.2uo.de/myths-about-urandom/
|
|
It looks like |
|
Not sure about windows and linux but OSX and FreeBSD use Yarrow, I think FreeBSD switched to Fortuna. Both the algorithms are made by the same person and are quite reliable. interesting link: |
|
Chiming in from the rafters... would there be a reason this would not make sense to implement in LibUV and be a downstream consumer to their implementation? |
paragonie-scott
commented
Mar 20, 2016
If you can guarantee rapid Node.js adoption of this (apparently new?) API in LibUV, this would probably be better, as there may be other applications that depend on it. |
That's not true. /dev/urandom is not the only source of entropy, especially when you have egd installed. Even when you don't, there's still a few extra bits of entropy gained by mixing in the results from getpid(), gettimeofday(), etc. Also, and I'm repeating myself for the third time now, if you all feel OpenSSL's PRNG is so horribly broken, then why are you not taking that up with the OpenSSL project? If OpenSSL switches, we switch - what's so difficult to understand here? |
alfiepates
commented
Mar 20, 2016
That's already been addressed. OpenSSL is a generally mediocre piece of software that's only really stuck around because we're already all using it and it's slightly less awful than rolling our own code in most cases... it's not a fantastic codebase at all and where we already have great CSPRNG (/dev/urandom) there's no point using code that has caused security issues in the past. We can sit around poking OpenSSL until they do something (which is not going to happen in a reasonable timeframe) or we can say "hey, here's an issue" and fix what we have the power to fix. |
|
So what you're suggesting is that, contrary to decades of good software engineering practices, we fix problems at the leafs instead of the root? I think you can guess from my tone how I feel about that. |
paragonie-scott
commented
Mar 20, 2016
Okay, here you go: openssl/openssl#898 They can read this thread for context. |
paragonie-scott
referenced
this issue
in openssl/openssl
Mar 20, 2016
Open
Make RAND_bytes a transparent wrapper for /dev/urandom #898
|
It's considered good form to give a summary in the bug report but thanks, I appreciate you took the time to open one. |
paragonie-scott
commented
Mar 20, 2016
Rest assured, they're well aware of the deficits in their userspace RNG. It's criticized quite frequently. I anticipate it will be swiftly closed as WONTFIX. |
Neither of those seem particularly random or unpredictable to me?
Those same decades of good software engineering practices recognize that if the root is unwilling to fix, you don't leave the problem lingering around, but go fix it at the leafs instead. |
co60ca
commented
Mar 20, 2016
|
There is no compelling argument against using kernel sources since OpenSSL is already seeding from it. As per: #5798 (comment) +1 |
|
I am particularly enjoying the idea that this issue is bubbling all the way down to making (u) random faster in the Linux kernel |
ircmaxell
commented
Mar 20, 2016
First off, As far as EGD, that gets its entropy from the kernel. And yes, So while there are benefits to kernel space CSPRNG (no userland compromise will leak state), there are no benefits of user space CSPRNG (since any compromise it would defend against by definition it would be exposed to, therefore it can't defend against them).
If that's the position of the project, that's your prerogative (all we can do is recommend and try to show you why we believe that stance is wrong). However, based on this thread, it seems like that's not really the position of the project but just your personal view (unless I'm missing something). |
|
Ok, so, one of the possible drawbacks that has been mentioned in openssl/openssl#898 (comment) is Is that critical for us? If yes, how would this proposal deal with it? |
paragonie-scott
commented
Mar 21, 2016
No.
If OpenSSL 1.1 (or later) adopts a |
|
Update: actually, Those are pretty close, so perhaps switching to /dev/urandom internally won't have a significant performance impact. This has to be tested when implemented, though, and on various platforms. If we care for |
ChALkeR
removed the
feature request
label
Mar 21, 2016
paragonie-scott
commented
Mar 21, 2016
Within reason, you should. If you only need to grab 32 bytes in under a millisecond, an upper limit on bandwidth measured in MB/s vs GB/s isn't likely to make a difference. Conversely, a difference that causes an app to take an extra 0.5s per execution would be a dealbreaker for most people. |
joepie91
referenced
this issue
in paypal/seifnode
Mar 22, 2016
Closed
Recommendation: Use the OS-provided random number generator #1
You must feel silly.
Can you share how you performed this test? e.g.
If you need a one-off then grabbing 32 bytes every now and again is fine. My machine can do that in a matter of microseconds using Point here is that throughput is important, and that getting consistent high throughput is more complex than it appears. Relying solely on
and I get a mere 96 Mbps (yes I attempted variations and this was the max throughput I could get). So can we provide a solid argument for switching implementations if the performance impact would be this great (as in can anyone empirically prove that security will improve with the new implementation, or is it mixed hypothetical at this point)? Or should this discussion be tabled until Here's the quick benchmark I threw together that has several variations: 'use strict';
const crypto = require('crypto');
const print = process._rawDebug;
const SIZE = 1024 * 1024 * 512;
var t = process.hrtime();
crypto.randomBytes(SIZE);
printTime();
//const CHUNK = 32;
//var t = process.hrtime();
//(function runner(n) {
//if ((n + CHUNK) >= SIZE)
//return printTime();
//crypto.randomBytes(CHUNK, () => runner(n + CHUNK));
//}(0));
//var t = process.hrtime();
//for (var i = 0; i < 16777216; i++) {
//crypto.randomBytes(32);
//}
//t = process.hrtime(t);
//print(((t[0] * 1e3 + t[1] / 16777216) / 1e4).toFixed(3) + ' ms/32 bytes');
function printTime() {
t = process.hrtime(t);
const mbps = (SIZE / 1024 / 1024) * 8 / (t[0] + t[1] / 1e9);
print(mbps + ' Mbps');
} |
|
alfiepates
commented
Mar 22, 2016
|
(Note: A previous partial version of this was sent out by email, please ignore that. Fuckin' GitHub.) Hey, I'm gonna chime in for the last time on this thread: I'm not going to name any names, but the tone of discourse in this thread (and in some cases externally, I've seen some... interesting tweets) risks becoming less than civil. While I'm not trying to derail the conversation towards "how2discuss", I think it's important we keep in mind that we (in my opinion) should have the same common goal: ensuring node is as secure as possible. Security is in the fairly unique position that compromises are rarely acceptable; While mediocre UI decisions will just lead to a bad user experience, or scrappy optimisation can cause a process to run longer or peg some cores, bad security leads to real life risk. To be frank: Shit security gets people killed. Node.js is deployed in situations where security is important, and therefore Node.js developers have a responsibility to ensure that security is up to scratch. This isn't about egos, this isn't about "I don't like how you're doing XYZ", this isn't about any of that. This is about risks that come with real-life consequences. There are people commenting in this thread who do security as their primary full-time employment, and who are damn good at it. I'd encourage all of you to look into the backgrounds of those in this thread making suggestions, not as an excuse to discredit their suggestions, but to put a human being behind these words. I'm gonna duck out of this thread since I don't have all too much to add implementation-wise, but I am more than willing (at a later date) to talk about how to make sure these discussions stay on track in the future. I appreciate what all of you are doing. |
@ChALkeR Try it with 3 or 4 instances running concurrently. A big issue with /dev/urandom is that it's a shared resource; it can produce output at a certain rate but that rate gets divided by the number of consumers. Also, /dev/urandom has seen some scalability improvements over the years but we support kernels all the way back to 2.6.18. |
Unfortunately
I'm not doubting you do, but I'm also letting you know that on my box I'm able to generate at ~300-400 Mbps. Demonstrating it'll be more complex to generate reliable throughput benchmarks, and that we may run the risk of making it slow enough that it won't be used. When users read blog posts stating const buf = Buffer(SIZE);
var idx = 0;
while ((idx = buf.writeUInt32LE(Math.random() * (-1 >>> 0), idx)) < SIZE);Which, by the way, generates data at 1300 Mbps. |
|
Ironically the above
Even tested well on cacert.org's rng results (search for "v8 4.9.385 Math.random()"). Maybe we should just switch to that. :-P |
|
@trevnorris That would be just another userspace PRNG which would be an extra signle point of failure on top of the system PRNG… I have not analyzed your
|
|
@ChALkeR I'm using 8.23, and nice trick.
Sorry. :-P was meant to imply /s or |
|
It's not uncommon for non-CS PRNGs to score well on randomness analysis.
|
paragonie-scott
commented
Mar 26, 2016
|
Humor is good, but unless the calendar reads April 1, please let's keep the discussion of "let's use Math.random() for crypto" to a minimum. |
Loosen up. There was no serious conversation about using it in core. My point is that if our implementation is so slow it's unusable then users will start resorting to methods like this. That I'm serious about, and history has proven users will take this type of action if our API doesn't work as needed and there's no alternative. Don't rebuttal that it's on them, because it's our responsibility to make sure our API is acceptable.
Then please share how randomness can be properly analyzed to see if it's suitable for cryptographic use. |
technion
commented
Mar 29, 2016
|
It's worth referring to LibreSSL's rewrite of OpenSSL's functionality. The first thing their getentropy() function tries (under Linux) is to pull from /dev/urandom: |
paragonie-scott
commented
Mar 29, 2016
|
Apparently OpenSSL bit Android too. |
|
Looks like a new paper was released regarding OpenSSL's PRNG, a few days ago:
|
|
@joepie91 I've scanned through the paper. The main premise seems to be that starting from a low entropy state reduces the efficacy of the PRNG? That seems self-evidently true. Node.js calls I don't really understand his other finding on reducing the entropy from 256 to 240 bits. It relies on the attacker being able to observe the PRNG's intermediate states? EDIT: Or is the issue that |
paragonie-scott
commented
Apr 26, 2016
|
I see this didn't make into Node 6. Maybe we could push for a true CSPRNG in Node 7? |
paragonie-scott
referenced
this issue
in DomBlack/php-scrypt
May 18, 2016
Merged
Update scrypt.php #49
|
Btw, this comment by @trevnorris:
is similar to reasons of using The reasons not to make it slower here are pretty similar. While 20% difference would likely be acceptable, making it several times slower would probably be viewed as a regression, and users would resort to various hacks. |
I don't think platform detection here for randomness source will be agreed on, so this should probably be first pushed up into one of the deps that handles platform abstraction for similar things — e.g. OpenSSL or libuv. |
|
I'm still waiting on a response of how to determine if data is "suitable for crypto". Since passing marks on randomness tests apparently aren't good enough. |
alfiepates
commented
May 24, 2016
|
@trevnorris The NIST has published a paper on this very topic, and maintains a somewhat easy-to-digest webpage covering CSRNG. But, in my opinion, this should not concern you, since (sensibly implemented) system randomness is designed specifically to abstract this away. However yes, I would somewhat agree with @ChALkeR in that the platform abstraction should probably be handled by a separate dependency, but that's also something I'm not entirely qualified to comment on so this is where my input ceases. |
|
@alfiepates Thanks for the links. The point is that I'm genuinely curious how |
|
@trevnorris |
|
@indutny sure, i'm just interested to see that for myself after it scored so high on the |
ircmaxell
commented
May 24, 2016
|
There is no test that will prove if a given function is random. There are only tests that prove it is not random. Meaning, no test will tell you if you are OK. That's important to keep in mind when talking about crypto and random numbers (specifically CSPRNG). |
|
@trevnorris It's important to realize that a CSPRNG shouldn't just be resistant to analysis, but also to targeted attempts at manipulation (eg. by 'poisoning' source entropy). No randomness test will show this. Determining whether random data is 'good enough' is largely a matter of theory. @bnoordhuis I haven't actually read the paper yet due to time constraints, rather I just figured I'd post it in this thread for its relevance. |
carlos8f
commented
May 25, 2016
|
Just a thought, if someone wanted to be malicious and had publish access to someone's dependency, they could quietly monkey patch: require('crypto').randomBytes = () => Buffer('pawnd')and ruin everyone's crypto in one line. maybe there should be a protection against this? |
|
@carlos8f Hmm. If that works, that'd work with any dependency, and you wouldn't even need to compromise a third-party one (due to the require cache). |
|
@carlos8f what about running |
carlos8f
commented
May 25, 2016
|
@indutny true, but that would be more noticeable of an attack. monkey patching core crypto module shouldn't be allowed imo. at the very least a warning should spit out to the console. |
|
@carlos8f well, having access to |
carlos8f
commented
May 25, 2016
|
@indutny yep, I tried to release a crypto node program the other day and just got laughed at. no one takes node security seriously, especially coming from the security community. PS i'm trying to revive the conversation on package signing, but seeing crap like this i quietly walk away.... |
|
@carlos8f It's not just about node, it is about java, go, python, ruby and almost anything that has a package managers. Any code that wasn't written by company's members should be considered as insecure, unless proven otherwise. |
carlos8f
commented
May 25, 2016
LOL that we're in the year 2016, and PHP is what we're aspiring to be like. And at least in PHP, you can't randomly redefine mission-critical parts of the system like random_bytes() without getting a proper exception in production. node could have better security, but it choses not to, because security is always someone else's problem. sadly companies don't audit their code, but that's why we hackers stay in business muahaha. |
There are legitimate use cases, like instrumentation and performance monitoring.
I wouldn't call /r/crypto a security community. Maybe "a bunch of of idiots that know just enough to be dangerous." In fairness, that applies to most technology-related subreddits with more than a few hundred subscribers. The larger they get, the more they regress to the mean. |
This nags at me. Having a proper random generator is so important, yet I've not yet been able to find a way to solidly empirically test an implementation. Which then makes the "good enough" line subjective and never ending (e.g. this thread). |
llafuente
commented
May 25, 2016
Anyone can freeze that object, so like @bnoordhuis I consider this a job for the user. I think node could have something to freeze all it's security-concern-objects/modules/***, and prevent unwanted modifications... |
alfiepates
commented
May 25, 2016
Which is why I linked the NIST paper. It's quite hard to tell if a set of numbers is random if all you have is that set of numbers, but if you give the method by which the set of numbers is generated, you can start to decide if you're getting randomness or not. Sure, there's no "magic randomness test box", but the NIST paper does define a set of rules to test CSPRNG implementations. |
Aside from what @alfiepates said, this is really not any different from security in general, however much it sucks (and yes, I agree that this is irritating). In real-world code, there are no proofs that code is secure, definitely not through empirical testing - you can only keep trying to find weaknesses, and fix them as soon as you find them, even if they seem small or insignificant. "Vulnerability scanners" are famously useless for this reason. It's the same reason why for both CSPRNGs and other tooling, the general advice is "use the battle-tested thing that isn't known to be broken", as it is more likely to have approached 'perfect security' than the non-battle-tested options, assuming no explicitly contradictory information is available. It's all you can do, really, and it has to be done rigorously. |
How do you ensure that this happens before anything else gets a chance to tamper with it, though? You'd have to expect the user to explicitly do this at the top level of their application code, and that is going to inevitably be forgotten. I'd rather see it frozen by default (at least to the point of not allowing to modify existing properties, since eg. promisifying might be necessary), with a runtime flag to allow certain files or modules to bypass this. That would cover instrumentation and performance monitoring, both of which are things that are typically explicitly enabled on runtime in a specific environment. |
|
That's fake security. One obvious way to circumvent it is to spawn gdb or open We're getting off-topic, though. |
azet
commented
May 27, 2016
•
|
FYI: similar discussion in Ruby - https://bugs.ruby-lang.org/issues/9569 Userspace RNGs are dangerous. Let me reiterate what a couple of people already stated: on Linux you want to seed from One core maintainer even suggested "adding entropy" via
Then I remembered why it exists: a long time ago -- before single threaded frontend languages were hip to write security sensitive "microservices" in -- SunOS (i.e. Solaris) lacked a kernel supplied random number generator. I remember I couldn't compile OpenSSH on this thing because of it and had to resort to some hacked-up rng/entropy gathering daemon back then. I decided to use As several people have already told you: the RNG in OpenSSL isn't fork safe, since this language depends on forking for performance, it's rather unwise to use a Userspace RNG that has known weaknesses. The recent paper on RNG weaknesses in OpenSSL was linked to in the thread already, but again: https://eprint.iacr.org/2016/367.pdf as it seems @bnoordhuis didn't read beyond the abstract or just didn't grasp it's meaning. Yes that'll affect node security. I think it's been fixed (which doesn't mean this release won't be around for ages in legacy systems,..) - yet it demonstrates how a Userspace RNG can fail to provide proper security for a language as a whole. Suggesting user-land entropy gathering daemons along is just pure insanity. There're so many attack vectors. So you either believe the Kernel you're running on is working properly (otherwise why would you run language X on it anyway - remember Ken Thompson's Turing Award Lecture? If you develop on e.g. a garbage collected, dynamically interpreted language - the same holds true for the OS you're running on. If you compile, in this case via multiple steps, you don't only have to trust your compilers and linkers, you have to trust the operating system they're running on and it's libc as well [btw also the klibc]) and may think about using additional security fallbacks like implemented by To be honest: I don't really care about node.js and every time I need to install some service that depends on five different module/package managers and installs more than 200 dependencies I'm asking myself why I'm even bothering. But I know people run node in production, so it may be wise to think about security sensitive issues in the core language and library. |
I think you are mistaking the program for the protocol.
What on earth makes you say that? Please get your facts straight. The only time node forks, it's to call execve() immediately afterwards.
How about you explain it instead of making disparaging comments? Besides the tone of your post, the factual errors make me reluctant to take it on face value. |
azet
commented
May 28, 2016
•
Which protocol? Are you referring to https://www.openssl.org/docs/manmaster/crypto/RAND_egd.html? If so, again; why would you want to add entropy from a userspace source? What exactly are the use-cases?
Well. It's not a traditional fork.
These seem to be tracked via PIDs (are these internal to node.js or actual OS PIDs?). I'd assume the latter given a subshell is openend from another process, but I'm certainly not as familiar with node.js internals as you are. libuv uses OS PIDs if I remember correctly. The documentation also contains a Further down the line, I read a bit of nodejs core code: // Ensure that OpenSSL has enough entropy (at least 256 bits) for its PRNG.
// The entropy pool starts out empty and needs to fill up before the PRNG
// can be used securely. Once the pool is filled, it never dries up again;
// its contents is stirred and reused when necessary.
//
// OpenSSL normally fills the pool automatically but not when someone starts
// generating random numbers before the pool is full: in that case OpenSSL
// keeps lowering the entropy estimate to thwart attackers trying to guess
// the initial state of the PRNG.
//
// When that happens, we will have to wait until enough entropy is available.
// That should normally never take longer than a few milliseconds.
//
// OpenSSL draws from /dev/random and /dev/urandom. While /dev/random may
// block pending "true" randomness, /dev/urandom is a CSPRNG that doesn't
// block under normal circumstances.
//
// The only time when /dev/urandom may conceivably block is right after boot,
// when the whole system is still low on entropy. That's not something we can
// do anything about.
inline void CheckEntropy() {
for (;;) {
int status = RAND_status();
CHECK_GE(status, 0); // Cannot fail.
if (status != 0)
break;
// Give up, RAND_poll() not supported.
if (RAND_poll() == 0)
break;
}
}If you remember the abstract of the paper referenced earlier, that exactly the issue at hand:
https://github.com/nodejs/node/blob/master/src/node_crypto.cc#L268-275: bool EntropySource(unsigned char* buffer, size_t length) {
// Ensure that OpenSSL's PRNG is properly seeded.
CheckEntropy();
// RAND_bytes() can return 0 to indicate that the entropy data is not truly
// random. That's okay, it's still better than V8's stock source of entropy,
// which is /dev/urandom on UNIX platforms and the current time on Windows.
return RAND_bytes(buffer, length) != -1;
}https://www.openssl.org/docs/manmaster/crypto/RAND_bytes.html says:
So I think the check should be Let me conclude saying that every cryptographer and security software engineer I've talked and most languages to prefer to seed from urandom directly. Possibly making use of safe and portable constructs like that of In future versions of the Linux Kernel Recommended reading material: https://emboss.github.io/blog/2013/08/21/openssl-prng-is-not-really-fork-safe/ |
EGD the protocol is implemented by several programs that collect or distribute entropy, e.g., low-entropy VMs that get their initial state from a hardware RNG on the network.
Indeed it is not. OpenSSL not being fork-safe is not relevant because node doesn't fork.
See #5798 (comment) - if you think my understanding of the paper and why I think node is unaffected is wrong, please explain so in detail.
Context is everything. The code you pasted seeds V8's Math.random() and the hash table seed. If you thought it had anything to do with the crypto module - it does not. I assume you are aware of this but Math.random() is not cryptographically strong, it's just a simple PRNG. The hash seed is to mitigate hash table collision attacks, primarily for string key collisions in JS objects in dictionary mode. If node didn't override the defaults, V8 will try to open /dev/urandom but happily continues with almost zero entropy if it fails - which, per the contract of Math.random(), is perfectly acceptable. The situation on Windows was even worse at the time of writing - processes spawned within one second of each other would generate the same random numbers - and is still not great: it uses rand_s / RtlGenRandom, which does not claim to be strongly random. Again, perfectly acceptable but not exactly great. Knowing what you know now, wouldn't you agree that OpenSSL's RNG is better?
Seeding from /dev/urandom (and /dev/random and /dev/srandom) is exactly what OpenSSL does. Why then do you consider it so broken?
I've commented on that before but to reiterate: that doesn't help while we still support 2.6.18 and 2.6.32. Performance regressions on older kernels are not acceptable.
I'd appreciate it if you could back up that claim with names and places. I've never heard any of the OpenSSL developers publicly state that. |
azet
commented
May 28, 2016
•
Can you please add a reference to that statement? What are you exactly referring to?
Have you read further down? My point being node builds on libuv, libuv uses threads, OpenSSL is not thread safe. Also it's rather easy to produce a fork bomb with node using
Context? The code I pasted has a comment above it saying it uses
I do not understand this sentence. And
No. Take a look at
Because it does not stop there, it seeds from it then mixes in "entropy" from user-land that can easily be tampered with, hence producing predictable output. See the very last link in my previous comment. There're many other examples.
I absolutely agree there. These are patches being worked on right now, they won''t be around for a while. Can you explain which performance regressions you're experiencing on older kernels specifically?
I have not done so on purpose, I thought that was obvious. Why do you think both LibreSSL and BoringSSL (now powering all of Google, including desktop applications [e.g. Chrome] et cetera) have switched to other RNG designs? |
This is wrong.
Irrelevant. Why do you even bring it up?
I think you should go back and read what I wrote again (hint: not 'from'.) Please put more effort in your replies, will you?
Can you explain? If by 'tamper' you mean 'attach with ptrace', then predictable output is the least of your worries. If you mean 'exploitable program bugs', the kernel is no stranger to that either.
Try reading from /dev/urandom from multiple processes simultaneously and you'll see what I mean. It's slow - very slow - and can have a dramatic impact on overall system performance, not just the readers.
I hope you understand I won't be able to just take your word for it. |
paragonie-scott
commented
May 28, 2016
•
|
This is getting nonproductive, fast. I have a lot of work on my plate right now, but once it's done, expect a patch so we can stop discussing the theoretical realm. The patch I'm envisioning will do exactly what libsodium 1.0.11 and newer (not yet released) does:
|
|
@azet
Update: I have made a mistake here, see explanation below: #5798 (comment). |
|
@paragonie-scott Thanks. I'm not sure though about pulling in the platform abstraction here instead of a dependency (openssl or libuv, for example). Also, what you proposed is going to affect speed, and may cause significant performance drop on Linux, which is bad even from the security side. Perhaps we will need tests, though. |
azet
commented
May 28, 2016
•
No it is not. Which version of OpenSSL is nodejs using? git-master? Take a look at the commit history in general from the RNG in question: https://github.com/openssl/openssl/commits/master/crypto/rand/md_rand.c The new threading API was added to 1.1.0-pre5 on 8th of march this year: openssl/openssl@8eed7e8 Before that you had to explicitly call
So it seeds from urandom and mixes into Math.random()? That's still a completely unacceptable design.
I'm aware /dev/urandom is currently slow. That's not the point. As suggested previously: take a look at
Sure. Again: look at their commit log, those of BoringSSL et cetera. I'm not going to name devs. - it doesn't even matter, development and changes are public. BTW: I'm still waiting for an answer on EGD - I'm not sure what you mean. |
azet
commented
May 28, 2016
Thanks for the information. So how does it work if multiple child processes want to read from |
|
@azet each child process starts from scratch without sharing anything except an possibly std input/outputs. So it just seeds the entropy from the kernel's RNG. |
technion
commented
May 29, 2016
Regardless of the rest of the discussion, this is objectively wrong. /dev/urandom does not block. I'm loathe to refer to the generally poor man page, but from random(4):
|
paragonie-scott
commented
May 29, 2016
|
It's never acceptable to read from |
azet
commented
May 29, 2016
•
Thanks for the explanation, and I've tested this on a 12core/2thread, 128GB machine over night. And it seems to be fine given a recent and well maintained OpenSSL release (debian jessie). BTW the only reason this is the case - as far as I can tell - is that commit openssl/openssl@3cd8547 has been added in 2013 to mitigate a problem on Android. With earlier releases it may still have been possible to exploit the PID-overlap issue and non-existent thread safety in the RNG, if I'm not mistaken. The thing is, as explained before, as per your seeding mechanism, if
I agree.
Unless you're planning on runnning node in initramfs there's really no reason to think about that. As for embedded devices: it largely depends, this is usually manufacturer error. They still have noise sources and interrupts, someone just forgot to configure the kernel properly or submit a patch. In any case this is nothing that node itself can do about, it's a proper upstream problem. I don't like the polling idea,
Exactly. And I'm thus not sure what @bnoordhuis is referring to. All these have been deprecated, the OpenSSL interface is ancient and their support for it has been dropped. |
paragonie-scott
commented
May 29, 2016
Agreed. I previously ported a significant chunk of libsodium's |
|
@azet Also, OpenSSL |
ChALkeR
referenced
this issue
Jun 6, 2016
Closed
Expose RAND_poll / RAND_add to user code via crypto module. #4014
zzxz
commented
Jun 24, 2016
•
|
A little late to the party, but if this helps, the folks over at Chromium had a similar discussion regarding the CSPRNGs. If I read it correctly, they dropped OpenBSD's RC4/ARC4 for kernel-space sources. The current implementation (as clarified here) appears to grab hardware-based entropy – if available – before reverting to standalone I think this is similar to what @paragonie-scott is aiming to patch, but I'm unsure if the RC4 flaws have been addressed? Thank you all for doing your best to keep us secure. Edit: Intel no longer calls it RDRAND for various reasons. ;) |
|
Looks like Linux urandom will now be ChaCha20-based: http://lkml.iu.edu/hypermail/linux/kernel/1607.3/00275.html |
oconnor663
referenced
this issue
in keybase/node-saltpack
Aug 1, 2016
Merged
{server,browser}-side saltpack encryption/decryption #2
|
(Loosely related): yet another userspace prng has falled down: |
|
/cc @saghul — would something like #5798 (comment) make sense in libuv? |
|
@ChALkeR I think it would. Moreover, I was planning on doing it myself, since I implemented a similar thing for another project not so long ago. The key will be documenting what guarantees we promise. I'll try to make a quick stub this week to ge the conversation started over there. |
saghul
added a commit
to saghul/libuv
that referenced
this issue
Sep 15, 2016
|
|
saghul |
99bc915
|
|
Not 100% ready yet, but feedback is more than welcome: libuv/libuv#1055 |
saghul
added a commit
to saghul/libuv
that referenced
this issue
Sep 15, 2016
|
|
saghul |
3d69382
|
saghul
added a commit
to saghul/libuv
that referenced
this issue
Sep 15, 2016
|
|
saghul |
e2f0aa1
|
saghul
added a commit
to saghul/libuv
that referenced
this issue
Sep 15, 2016
|
|
saghul |
1bc53ea
|
|
For those that believe Linux's I haven't seen a single compelling reason in this thread to switch away from OpenSSL's PRNG, its a platform abstraction layer on top of platform-specific entropy sources... seems like exactly what we want. Also, its easy to complain about some kind of perceived "low quality" of OpenSSL - but suggesting that the Node.js team has a better cryptographic background than their devs, and that we should be writing our own PRNG abstraction layer is a bit tenuous, IMO. We certainly wouldn't get the scrutiny that they do. |
paragonie-scott
commented
Sep 16, 2016
•
That's not the argument, at all. The argument is this:
That's a very different argument than "there will never be a Several security experts have chimed in on this thread. The consensus is unanimously in favor of using the kernel's CSPRNG over OpenSSL's userspace PRNG.
I linked several research papers above. Did you read through them all?
If that were true, there'd be no complaint. But that's not what's going on under the hood. OpenSSL's What you want is something like libsodium's sysrandom. @Bascule previously wrote a Ruby gem, called sysrandom, which is a secure replacement for Ruby's SecureRandom (misnomer, also uses OpenSSL). I've written about this in a blog post appropriately titled, How to Generate Secure Random Numbers in Various Programming Languages.
You're right. We should just use libsodium's, which does the job better than any other implementation I've seen.
Until last year, I'd have taken this as tongue-in-cheek, with Heartbleed and whatnot. |
User-space gives defence against locking problems in urandom, as you have been told, and is one of the objections to OpenSSL itself shifting over to direct read of urandom. A well-constructed PRNG cannot magically add entropy, but neither does it magically subtract. Node implementing its own platform abstraction for entropy also adds an additional point of not-well-reviewed failure, so this point is not compelling without demonstrating that OpenSSL currently has security flaws that effect node and need to be fixed by bypassing it.
Irrelevant, node doesn't fork, why do people keep bringing this up? Are there other, relevant, criticisms?
You should keep in mind that it is impossible in this thread to distinguish between security experts, and people just playing them on the internet. Also that security experts are contributors to OpenSSL. Who do you think are security experts, and why? |
|
Aside: Assuming we're not already doing so, it might be a good idea to add the |
paragonie-scott
commented
Sep 16, 2016
•
In no particular order:
Additionally, there are plenty of people I consider colleagues worth mentioning because:
And so the list expands to include:
Feel free to ask anyone on that list if they'd prefer OpenSSL's userspace PRNG or the operating system kernel's CSPRNG for cryptography purposes. I guarantee you'll get 100% in favor of urandom (and urandom equivalent). Feel free to try to find cryptographers and real world security experts to offer a dissenting opinion. By and large, you'll be more successful at finding folks who agree with me than disagree with me (only on this particular point). |
paragonie-scott
commented
Sep 16, 2016
|
(Corrected a mistake in my previous comment and wanted to make it known that there are probably about 50 - 100 other people I could have listed there too, but the list is plenty long enough.) |
|
OK, I also got the opinions of someone I know to be a security expert (the requirement is a successful track record of breaking systems), and they agree with you (and know you): current best practice is direct read from O/S entropy sources. Whether that is best for Node.js though, is not as clear, there are other factors to consider.
|
paragonie-scott
commented
Sep 16, 2016
•
|
1, 2, and 4: 3: If the two are in conflict, security should take precedence over performance when it comes to cryptography. However, a significant performance penalty just invites DoS attacks (which is a security problem). So I agree, this should be addressed. Fixing the issue upstream would be the best solution (since it'd pay forward to lots of programming languages), but in the absence of an upstream solution, what do we do? A. Keep using a userspace PRNG and not exposing an alternative CSPRNG interface? That's for the Node team to decide. I'm jumping over the OpenSSL repository to discuss getting a patch ready to expose this API in the next version. |
|
I'd like to clarify that while libuv might get this API I didn't implement Moreover, even if the libuv patch lands tomorrow (which will not happen On Sep 16, 2016 23:52, "Scott" notifications@github.com wrote:
|
I just want to point out here that there are plenty common non-HTTPS usecases for |
co60ca
commented
Sep 17, 2016
|
To @joepie91 I've seen a implementation of UUID v4 use math.random so yes, a secure default that pulls from system entropy would be fantastic. |
atoponce
commented
Sep 17, 2016
|
As another example of using crypto.randomBytes() in a non-HTTPS setting, I am using it in a password generator: https://github.com/atoponce/nodepassgen. |
grahamc
referenced
this issue
in NixOS/nixpkgs
Nov 19, 2016
Closed
[wip] nodejs: Flag to disable building with openSSL #20513
|
ping @nodejs/crypto ... should this remain open? |
|
No, let's close. |
bnoordhuis
closed this
May 30, 2017
azet
commented
May 30, 2017
•
|
this issue remains unfixed and there has been valuable discussion on resolving the problem. OpenSSL's implementation remains, to my knowledge, largely untested and specifically optimized to cater to TLS implementation's needs for fast random numbers. most languages (e.g. python) opt to use the I urge the core team to use libsodium's reviewed, platform-aware way to obtain secure pseudo random numbers: https://github.com/jedisct1/libsodium/blob/master/src/libsodium/randombytes/randombytes.c please re-open this security relevant issue. |
tarcieri
commented
May 31, 2017
|
What was the rationale for closing this issue? |
|
If I understand correctly (which is a big "if" when it comes to cryptography and related issues): Especially given activity on openssl/openssl#898 fairly recently, and that progress there could mean a suitable fix here that can still use OpenSSL APIs, I'd be for keeping this open. |
|
@nodejs/crypto I think we should keep it open until its fixed. Last discussion was that OpenSSL were themselves moving this direction, and we hoped to simply inherit their fix. From openssl/openssl#898, it does indeed look like they are moving along (slowly, but with recent progress). But if OpenSSL 1.1 doesn't change this, we could revisit whether node should use some other way of getting pseudo-random. |
|
As a handful of people (including collaborators) have chimed in about wanting to continue the discussion. I'm reopening That being said I think this might be part of a larger conversation about openssl... perhaps we can look into improving their implementation on systems that have support? afaict libsodium is going to be a nonstarter if it doesn't have fips support |
MylesBorins
reopened this
May 31, 2017
paragonie-scott
commented
May 31, 2017
Replacing OpenSSL entirely with Libsodium might be a nonstarter. Is FIPS-140-2 compliance an absolute mandatory requirement? Because ECB mode is FIPS compliant, and you sure as hell don't want people encrypting that way. |
technion
commented
May 31, 2017
|
Any changes to OpenSSL, even if they improve in this space, are going to show up on a new major release, which moves much, much slower than Node. If they made a great release tomorrow, it will take years to hit some distributions at all. I don't feel you want to wait on this. |
Node.js doesn't use the operating system's OpenSSL. Node.js ships with its own copy of OpenSSL. |
|
I might be open to bundling libsodium in addition to openssl but that should be its own issue because there will be numerous details to hash out. We sure as heck are not going to ship something homegrown and until openssl grows the requisite APIs there isn't anything actionable. I'm closing this again and I'm locking it to prevent this already humongous thread from growing bigger. |

speakeasypuncture commentedMar 19, 2016
randomBytes uses OpenSSL as its random number generator. It would be wiser and less errorprone to use a system RNG like urandom on Unix platforms, getrandom syscall on Linux and CryptGenRandom on Windows.