I've been going through this Unciphered article on Randstorm and honestly, I'm still pretty lost.
So from what I gather, wallets made with the BitcoinJs library between 2011 and 2015 have some serious issues because of how they handled randomness. The worst of it seems to be with wallets created between May 4, 2011, and March 2012.
But the article doesn’t really clarify what the actual exploit is. It basically boils down to the fact that they used Math.random() for generating random values before March 2014, and we all know that's not a good function.
If we check out the initial commit from March 4, 2011, eckey.js is what generates the private key, while rng.js and prng4.js in the jsbn folder are used to gather randomness.
In rng.js, if rng_pool isn’t initialized, it gets filled with random values from Math.random().
Now, Math.random() has a cycle of 2^60 before it repeats, which is mentioned in the article. They also say it doesn't pass modern benchmarks, but I'm a bit unclear on what those benchmarks are.
So is Math.random() the main issue here? What’s the real weakness?
Later on, they seed the pool with the current time in milliseconds, and then they initialize the state and use the pool as the key for the RC4 cipher from prng4.js.
That script creates a 256-value array and fills it through a loop.
eckey.js then uses SecureRandom() to create our private key.
But again, this doesn’t really shed much light on the actual vulnerability or what potential attacks could look like. The article hints that if we have a GUID or IV (which I assume is a public key?), then we might only need to deal with 2^32 to 2^64 values (2^48 seems to be the most common). What does all this really mean?
What's the deal with Randstorm vulnerability?
10 replies 149 views
Weird post. This isn't a new issue, here is a talk I gave in 2015 describing exactly this vulnerability https://youtu.be/TYQ-3VvNCHE?t=3072 (including that it never uses window.crypto in that code). At the time of my presentation the code in question was already widely known to be insecure and deprecated. So it's pretty weird that they're attributing the discovery to 2018, years later.
As far as what attacks might be used-- the state space is simply too small: an attacker can throw computing power at it to search the possible random numbers, generate the resulting keys, and check the blockchain for them.
But I think the specific issue is a distraction. It's simple: DO NOT USE JAVASCRIPT FOR CRYPTOGRAPHY. If you've used JS with any private key, assume it compromised.
No programmer, no matter how skilled can write JS code that doesn't leave secrets laying around in memory. No programmer, no matter how skilled can write JS code that is sure to not leak secrets via side channels.
Protecting against memory leaks and timing sidechannels are basic steps that any competent author will take.
Thus, by definition, no JS cryptography software is competently written.
You shouldn't use crypto software that wasn't competently written even if at the moment no one knows anything specifically wrong with it.
With this handy rule of thumb at hand you don't have to worry about the RNG embarrassment discussed in that post.
Let me get this straight. In rng.js , our pool rng_pool is seeded first with the time the browser starts in the line 37 like
with 48 bits of entropy
and then it is seeded in with the time in milliseconds whenever the rng_seed_time() is called
(does it bring any new bits of entropy?)
The rng_pool is an array that is used to initialize our state rng_state in the line 52
and rng_pool is the input for that initialization. It is being inputted into Arcfour() from prng4.js which in line 32 returns
and this is what is being assigned to rng_state at the end of it
Then in ecdsa.js it becomes just rng in the line 128 like:
and in the line 180 we construct an integer value from that array with BigInteger(...). After this, the integer is passed to eckey.js for generating the private key? (i.e. after BigInteger(...) we already got our private key. Its output is essentially our private key)
On the development side, on cryptography in JavaScript, yeah. Your point is understandable. Even Mozilla developer doc recommends instead to use Web crypto API
I spent some time on it and here is what I understand.
Indeed Math. Random is called when a private key is generated, however, it's called many times in a loop. The result of math.random() varies because of the state variable used in Math.Random varies every time it's called. (The implementation of math.random depends on the browser)
while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
t = Math.floor(65536 * Math.random());
rng_pool[rng_pptr++] = t >>> 8;
rng_pool[rng_pptr++] = t & 255;
This in turn is mixed with the time millisecond when the key is generated, I think there could be a small space to search when we know the exact time of key generation otherwise, I am assuming that the keyspace is large to search.
Yes, it is in the browser. But you cannot just ignore it. Classically-- as in when this code was actively used-- math.random was a 48bit lcg seeded by the browser time at start. It doesn't matter how many times you call it because math random itself only had 48-bits of state.
It's not only because of that, but also because (node)JS likes to create oodles of files when it installs dependencies, and any one of these packages can be bugged, even if you pin all your dependencies to a specific version.
Because there might be a dep of a dep of your dep that is not pinned, and someone takes control of the project and publishes a malicious code to steal keys in your code, and hence everybody else's code.
Plain JS also has this problem, because most of the time you are relying on minified bundles that are hard to read and might be hiding a malicious code.
Yes, JS has a dependency culture which is pretty much incompatible with security, though it's not alone-- Rust is also in that boat (and so I find it a bit alarming to see rust promoted as a solution to security problems.)
HyperCipherFull Member
Posts: 220 · Reputation: 780
#8Dec 6, 2021, 09:58 AM
👀
Ser, I merely want to ask a simple question.
You said that,
and that Rust is also "in that boat", does that actually mean that all of those new, "more modern" blockchains with clients that were written wholly and/or party in Rust are also vulnerable to such leaks, and that users should avoid holding any assets in those blockchains?
Of course. If you use unsafe cryptography, then it will bite you. For example:
Does it mean that C++ is unsafe? No. It means, that if you use "std::rand()" from the standard library, then it will be unsafe, because then your seed is only some 32-bit timestamp, and all keys, generated in this way, can be sweeped in a moment. And the same is true for other languages and tools: if you use built-in randomness from many languages, and it uses small seeds, then it will hurt you. That's why Satoshi included OpenSSL, instead of generating private keys in some unsafe way, as shown above.
Edit: example straight from Rust: in a standard library, you have this function: https://docs.rs/rand/latest/rand/trait.SeedableRng.html#method.seed_from_u64
Guess what: if you use it, then your private key will be as weak, as the puzzle #64, if not weaker. And guess what people might do: they can fill this value with the current timestamp, and boom, everyone using it to create private keys is doomed.
You can write rust code that zeros memory that contained secrets and has constant time behavior wrt secrets. It's in the same boat as C/C++ which is in practice it works with some care and review but compilers may from time to time undermine you which is why review effort to check that they're not is required.
Of course, if you use not fit for purpose (or backdoored) dependencies you're screwed... which I think is a lot more of a risk in rust due to the extreme overuse of dependencies. That's not a problem with the language itself though, it's a problem with the culture that comes along with the language.
In Rust at least you *might* be able to get away with this by implementing as many things as possible in your own library, and then you don't have to worry about any dependencies at all.
Being bare-bones like C/C++ certainly gives it those freedoms.
Whereas in a language like JS it is practically impossible to implement everything yourself and still be secure since chances are you are going to be downloading some packages just to make the user's runtime work anyway. Not to mention the giant attack surface that browsers and anything resembling a browser like Electron brings.
Related topics
- Bitcoin and Quantum Threat: No Big Deal Yet 3
- Bitcoin Core Security Advisory and 10 Older Vulnerabilities Revealed 5
- $77M Exploit of eBTC on Monad: $870K Stolen 10
- Taiko reopens bridge after $1.7M exploit, assures users are compensated 0
- IRS crypto tax audit notice 10
- Proof-of-Reserves (PoR) isn’t a real audit 9