Author

Topic: What is exactly Randstorm vulnerability? (Read 231 times)

legendary
Activity: 1568
Merit: 6660
bitcoincleanup.com / bitmixlist.org
August 01, 2024, 04:13:47 AM
#11
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?
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.
staff
Activity: 4284
Merit: 8808
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?
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.

copper member
Activity: 906
Merit: 2258
Quote
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
Of course. If you use unsafe cryptography, then it will bite you. For example:
Code:
#include
#include
#include

int main()
{
    std::srand(std::time(nullptr));
    for(std::size_t i=0;i<32;++i)
    {
        int value=std::rand()%256;
        std::cout.width(2);
        std::cout.fill('0');
        std::cout<    }
}
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
Quote
Code:
fn seed_from_u64(state: u64) -> Self
Create a new PRNG using a u64 seed.

This is a convenience-wrapper around from_seed to allow construction of any SeedableRng from a simple u64 value. It is designed such that low Hamming Weight numbers like 0 and 1 can be used and should still result in good, independent seeds to the PRNG which is returned.

This is not suitable for cryptography, as should be clear given that the input size is only 64 bits.

Implementations for PRNGs may provide their own implementations of this function, but the default implementation should be good enough for all purposes. Changing the implementation of this function should be considered a value-breaking change.
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.
legendary
Activity: 2898
Merit: 1823
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.

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.)


 👀

Ser, I merely want to ask a simple question.

You said that,

Quote

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.


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?
staff
Activity: 4284
Merit: 8808
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.

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.)
legendary
Activity: 1568
Merit: 6660
bitcoincleanup.com / bitmixlist.org
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.

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.
staff
Activity: 4284
Merit: 8808
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)
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.
jr. member
Activity: 31
Merit: 5
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.

newbie
Activity: 28
Merit: 84
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

Code:
t = Math.floor(65536 * Math.random());

with 48 bits of entropy
and then it is seeded in with the time in milliseconds whenever the rng_seed_time() is called

Code:
rng_seed_int(new Date().getTime());

(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

Code:
rng_state.init(rng_pool);

and rng_pool is the input for that initialization. It is being inputted into Arcfour() from prng4.js which in line 32 returns

 
Code:
return this.S[(t + this.S[this.i]) & 255];

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:

Code:
var rng = new SecureRandom();

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

staff
Activity: 4284
Merit: 8808
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.


newbie
Activity: 28
Merit: 84
I've read the article from Unciphered about it, multiple times, and still fail to understand it

It basically says that wallets generated by BitcoinJs front end library from 2011 to 2015 are vulnerable because of the 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()

Code:
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 the 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

Code:
function rng_seed_time() {
  rng_seed_int(new Date().getTime());
}

And later for

Code:
SecureRandom.prototype.nextBytes = rng_get_bytes;

we initialize the state, and pass the pool as the key into the RC4 cipher

Code:
rng_state = prng_newstate();
rng_state.init(rng_pool);

from prng4.js

prng4.js

which creates a 256 values array

Code:
this.S = new Array();

and fills it with the loop

Code:
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

Code:
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:

Code:

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();

Code:
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)
;
},
.
Jump to: