What exactly is the Randstorm vulnerability?
I've read the article from Unciphered about it, multiple times, and still fail to understand it.
It basically says that wallets generated by the BitcoinJS front-end library from 2011 to 2015 are vulnerable because of poor randomness generation. (Especially those generated between May 4, 2011 to March 2012.)
But it's really vague on explaining what the actual exploit is. It could be just summarized as: it used Math.random()
for randomness before March 2014, and it is a bad function
Let's look at the initial commit from March 4, 2011 : eckey.js
is used for generating the private key, while rng.js
and prng4.js
in the jsbn
folder are used for harvesting randomness.
rng.js
If rng_pool
is not already initialized, it is filled with random values from Math.random()
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;
}
Math.random()
according to the article has a cycle of 2^60 values before they repeat. The article also mentions that it fails modern benchmark tests, but I'm not sure about them.
Is Math.random()
the whole weakness of the story? What is the weakness actually about?
Later, the time in milliseconds is seeded to the pool
function rng_seed_time() {
rng_seed_int(new Date().getTime());
}
And later for
SecureRandom.prototype.nextBytes = rng_get_bytes;
we initialize the state, and pass the pool as the key into the RC4 cipher
rng_state = prng_newstate();
rng_state.init(rng_pool);
from prng4.js
prng4.js
which creates a 256 value array
this.S = new Array();
and fills it with the loop
for(i = 0; i < 256; ++i) {
j = (j + this.S[i] + key[i % key.length]) & 255;
t = this.S[i];
this.S[i] = this.S[j];
this.S[j] = t;
}
eckey.js
eckey.js
uses SecureRandom()
and creates our private key
var rng = new SecureRandom();
....
this.priv = ECDSA.getBigRandom(n);
But again, this tells us next to nothing about the actual vulnerability and what attacks might be used. Unciphered's article suggests that if we have GUID or IV (I guess that's a public key?), then we can do the work with just 2^32 to 2^64 values (2^48 most commonly).
Also, not sure about the clicks being added in the entropy pool, apart from:
<body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
comment.
In what way, other things are added into entropy pool apart from the initial timestamp seed?
Edit July 23, 2024:
Sorry, I forgot that ecdsa.js also has its own context
ecdsa.js
Basically, getBigRandom()
method is realized in this file with rng = new SecureRandom();
Bitcoin.ECDSA = (function () {
var ecparams = getSECCurveByName("secp256k1");
var rng = new SecureRandom();
....
var ECDSA = {
getBigRandom: function (limit) {
return new BigInteger(limit.bitLength(), rng)
.mod(limit.subtract(BigInteger.ONE))
.add(BigInteger.ONE)
;
},
.