Brute-forcing this is similar to brute-forcing all possible keys, regardless of whether you consider the timestamp or not. you might need much more information then timestamp!
If you believe the timestamp is a critical factor, you might be mistaken. The process performs an XOR operation on a randomly generated number before the seedInt call. Additionally, seedInt is called twice: first, when the browser window loads, and second, 1–2 minutes later when the user clicks the "Generate Wallet" button.
On this 256-byte array, it performs an ArcFour operation. When the user clicks a second time, it does not reseed with random values; instead, it calls arcFour.next, which flips bits again.
The only potential vulnerability was with Math.random() due to performance considerations. Math.random() was poorly designed, and because of navigator.appVersion < "5", it always fell back to Math.random() in most of browser.
However, given the above, it is still incredibly difficult to determine:
Which browser the key was generated on,
The exact timestamp at the time of generation,
Which algorithm was used by the browser,
The initial seed of that algorithm.
Most Math.random() algorithms had a repetitive nature within the 32-bit space, but it remains challenging to gather all the necessary information.
You also mentioned some methods to generate the next number for Node.js random functions. However, this vulnerability predates 2014, making it irrelevant because, before 2014, V8 and other engines used different algorithms instead of Xorshift. For example, V8 might have used MWC, while others may have used LCG.
Good luck with your search! I’ve already spent two days on this. 🙂
I even created code to mimic old browser random generation after some research (which might be useful) and attempted to brute-force using the bitcoinjs library implementation. However, due to the extensive number of loops, the attempts were too slow.
class LCG {
constructor(seed, a, c, m) {
this.state = seed & (m - 1);
this.a = a;
this.c = c;
this.m = m;
}
random() {
this.state = (this.a * this.state + this.c) % this.m;
return this.state / this.m;
}
}
class MWC {
constructor(seed) {
this.rngstate = seed;
}
random() {
var r0 = (Math.imul(18273, this.rngstate[0] & 0xFFFF) + (this.rngstate[0] >>> 16)) | 0;
this.rngstate[0] = r0;
var r1 = (Math.imul(36969, this.rngstate[1] & 0xFFFF) + (this.rngstate[1] >>> 16)) | 0;
this.rngstate[1] = r1;
var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0;
return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10;
}
}
const engines = {
firefox: () => new LCG(12345, 1664525, 1013904223, 0x100000000),
ie: () => new LCG(12345, 214013, 2531011, 0x80000000),
safari: () => new LCG(12345, 1664525, 1013904223, 0x100000000), // Same as Firefox
chrome: () => new MWC([12345, 56789]),
};
function testEngines() {
console.log("Testing random number generators:");
for (const [name, engineFactory] of Object.entries(engines)) {
const engine = engineFactory();
console.log(`\n${name} RNG:`);
for (let i = 0; i < 10; i++) {
console.log(engine.random());
}
}
}
// Run the test
testEngines();
IMO: I don't think it's worth trying unless you have some of the information I mentioned.
There are a few things I would like to mention.
Brute-forcing this is similar to brute-forcing all possible keys, regardless of whether you consider the timestamp or not. you might need much more information then timestamp!
If you believe the timestamp is a critical factor, you might be mistaken. The process performs an XOR operation on a randomly generated number before the seedInt call. Additionally, seedInt is called twice: first, when the browser window loads, and second, 1–2 minutes later when the user clicks the "Generate Wallet" button.
On this 256-byte array, it performs an ArcFour operation. When the user clicks a second time, it does not reseed with random values; instead, it calls arcFour.next, which flips bits again.
The only potential vulnerability was with Math.random() due to performance considerations. Math.random() was poorly designed, and because of navigator.appVersion < "5", it always fell back to Math.random() in most of browser.
However, given the above, it is still incredibly difficult to determine:
Most Math.random() algorithms had a repetitive nature within the 32-bit space, but it remains challenging to gather all the necessary information.
You also mentioned some methods to generate the next number for Node.js random functions. However, this vulnerability predates 2014, making it irrelevant because, before 2014, V8 and other engines used different algorithms instead of Xorshift. For example, V8 might have used MWC, while others may have used LCG.
Good luck with your search! I’ve already spent two days on this. 🙂 I even created code to mimic old browser random generation after some research (which might be useful) and attempted to brute-force using the bitcoinjs library implementation. However, due to the extensive number of loops, the attempts were too slow.
IMO: I don't think it's worth trying unless you have some of the information I mentioned.