Android RNG Weakness Renders Bitcoin Wallets Insecure
bitcoin.org
bitcoin.org
I don't know the Bitcoin software involved at all, but I can sketch out an attack that might shed some light on it, and, more importantly, instill an appropriate fear of DSA into you:
To generate a DSA key, you come up with primes p and q and a generator g, which process is a paralytic non-Euclidian brain injury I will not attempt to describe. Then you do like Diffie Hellman: generate a random private key x and from it a public value y = g^x % p. The pubkey that validates signatures is the tuple (p, q, g, y).
To sign, you generate a random k value, which must never be reused, Iä! Iä! never, and:
r = g^k % p % q
s = k^-1 (H(m) + x•r) % q
The signature is (r, s).If ever you should fail to heed these words and generate two signatures with the same k value, Iä Cthulhu Ftaghn! then simple high school algebra can be used to beat DSA. The attacker doesn't even need to know what the k was, and the attack is so fast you can just try it to see if k was repeated (I skipped the algebra and just dumped the formulas for the attack here):
H(m1) - H(m2)
k = -------------
S1 - S2
x = ((S1•k) – H(m1))• r^-1 % q
This bug (also in an ECDSA implementation) is what broke the Playstation 3, too.You see that comment on the Bitcoin thread about the repeated r-values; a repeated r-value (r as in the r parameter of a DSA signature) just tells you that someone repeated a k. Iä! Iä!
So anyone new jumping on this would have to race the one or more existing exploiters.
(seriously, downvoted for asking about stats on cryptopals? it is relevant -- #6 has two DSA questions!)
63 people have finished the crypto challenges; about 9000 people have started them.
https://docs.google.com/spreadsheet/ccc?key=0AscYGzcM4zJNdHR...
I've probably missed a few tweets and typo'd a few numbers along the way, but it should be mostly accurate. Note the tabs (at bottom left) for a derived speadsheet (percent of participants at each level) and a chart.
I don't know what I'll do for set 7 yet. Maybe Excel implementations of the SHA-3 finalists...
Most of the time this property is useless, because you want to verify the signature against a known-good key, but it does also mean you're probably not going to want to use DSA to pass signed covert messages.
Schnorr signatures actually have a simpler, and I think more intuitive, construction and don't have this property. They also don't require collision resistant hashes.
def verify(msg,sig,addr): return pubkey_to_address(ecdsa_recover(msg,sig)) == addr
http://blog.kchandrahasa.com/blog/2013/08/09/android-4-dot-2...
However I've read somewhere that now apparently even Android 4.2 is affected which would mean there's something more? Whoever knows more, please write more technical details.
Older versions of Android's SecureRandom could return the same value if (a) you were manually seeding SecureRandom and (b) no prior crypto operations were performed on that SecureRandom instance before seeding,
There was some (very) bad advice circulating on blogs which advised using this technique to generate local encryption keys from a seed, in order to obfuscate the key.
This was fixed in Android 4.2 when we switched from BouncyCastle to OpenSSL as the underlying crypto provider. I don't know why you'd still be seeing this on Android 4.2, but you shouldn't be doing this anyway. SecureRandom is seeded by the system. Manually seeding it is a bad idea. (And trying to force deterministic output from is a very bad idea.)
There's a blog post I wrote which goes into a bit more detail: http://android-developers.blogspot.com/2013/02/using-cryptog...
The linked article is a bit light on details, so I don't know if this is what they were doing or not. I doubt it though, since that would have meant they were seeding SR with the same value, and I'd like to believe the Bitcoin devs wouldn't make that mistake.
so i don't think the parent link explains the current issue.
This is a really common misconception people seem to have about cryptographic random number generation, and a topic Ferguson and Schneier do an extremely good job breaking down in _Cryptography Engineering_.
It'd be interesting to know what the bug is and if that bug affects Bouncy Castle and/or OpenSSL as well, or if Google screwed up the glue code somehow
blogpost about weakness in java.security.SecureRandom: http://armoredbarista.blogspot.com.au/2013/03/randomly-faile...
Simply initialising it is insufficient, because you need to call addProvider() (cf. https://developer.android.com/reference/java/security/Securi... ) to actually have any effect.
It purports to install LinuxSecureRandom as a new systemwide default (LinuxSecureRandom, lines 56-57).
Can you provide a link to something corroborating this?
Quote:
> Apache Harmony revealed multiple weaknesses caused by implementation bugs. As a part of Android a plethora of cryptographic functions [17] rely on this PRNG. One of the bugs addresses directly the Android platform, where as the second one only targets Apache Harmony.
† It's virtually certain not to be in the code, since Android's CSPRNG is based on OpenSSL now, not Harmony's built-in CSPRNG.
> You should not assume that using OpenSSL directly is safe. On Jellybean+ the SecureRandom provider is just a shim over the OpenSSL RAND_* functions and Jellybean+ is also affected. However TLS connections are OK.
> I realise there's going to be a lot of questions about what exactly is going on here, but I'm not on the Android team and can't talk on their behalf. It's up to them to document exactly how the RNG is broken. Suffice it to say, if you aren't sure if you're affected, you probably are but you are welcome to send me a private message detailing what you're doing and I'll let you know.
[1] https://bitcointalk.org/index.php?topic=271486.msg2911376#ms...
x = new SecureRandom();
y = new SecureRandom();
y.setSeed(predictable);
x.generateBytes(zz);
and also i think the seed is thread local so you can have:
x = new SecureRandom();
x.setSeed(xxx);
then on another thread x.generateBytes()
won't do what you think it will do. i don't think this would cause dupes because i assume apps are isolated from each other and these apps aren't calling setSeed...
[1] this is assuming android people haven't patched openssl to have different behaviour and also setSeed might be safe in openssl because it uses the value to augment and not replace the state.
https://bitcointalk.org/index.php?topic=271831.0
They claim the problem lies with 'a component of Android'. One of them told me that the solution was to switch from using SecureRandom to reading /dev/urandom directly. The actual source changes appear not to be public, and he wouldn't tell me details about the issue.
I tried to find the usages of SecureRandom in a couple of the apps that are supposed to be affected, but a cursory search didn't turn up much. I suspect it's inside some library that I don't know to look inside. The question, I guess, is whether all the affected apps share the same library or not -- I don't _think_ so, but if they do it would dramatically reduce my confidence that the issue lies with the Android platform, as claimed.
But one thing I'm wondering is: would any setSeed on any SecureRandom instance cause a potential problem? Or just on the same instance being used for signing? In other words, do I only need to check that spongycastle -- which I presume is a fork of bouncycastle -- handles things reasonably? Or could any other messing about with SecureRandom mess it up?
1) reading from /dev/urandom takes more than 10ms causing an entropy error
2) a malloc fails causing the error to be saved to the fallback ERR_STATE instead of the thread local ERR_STATE
3) java sees failing code and tries to read the last error but now malloc succeeds so it reads it from its local thread ERR_STATE which succeeds or some other thread has clobbered the fallback ERR_STATE
4) java doesn't throw an exception because it sees no error
the other possibility is the openssl random state is shared between processes due to forking. i hear there is a process called the zygote that warms up the vm and forks to create apps. if it initialises openssl then it is possible child processes could get the same random state.
...I'm the packager of SpongyCastle (a task a well-trained monkey could do, I don't alter the code other than package-renaming it), and I confess some relief that the issue is with Android's in-built SecureRandom class, rather than anything in (Bouncy|Spongy)Castle.
https://code.google.com/p/bitcoin-wallet/source/detail?name=...
So literally they're just ripping out SecureRandom and replacing it with their own stub that reads /dev/urandom.
ECDSASigner signer = new ECDSASigner();
ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, ecParams);
signer.init(true, privKey);
BigInteger[] sigs = signer.generateSignature(input.getBytes());
return new ECDSASignature(sigs[0], sigs[1]);
This _appears_ to be a correct invocation of spongycastle-formerly-bouncycastle, initializing the signer with an instance of ECPrivateKeyParametes, and NOT an instance of ParametersWithRandom, so that, on org/spongycastle/crypto/signers/ECDSASigner.java line 41, we call SecureRandom() with no arguments.I don't see spongycastle ever calling setSeed, and I don't see it ever leaking its SecureRandom instance, so unless calling setSeed on ANY SecureRandom instance is a problem, this _looks_ like a correct usage.
Also, I now remember how much I hate reading Java.
One easy way to shoot yourself in the foot with SecureRandom is to use seed values from a source with low entropy.
http://developer.android.com/reference/java/security/SecureR...
If one were to copy/paste the sort of code samples which show up when one Googles for [Android SecureRandom], one would more than likely call setSeed, possibly in such a fashion as to duplicate seeds and, predictably, make the output of the random number generator deterministic.
I will leave it to one's individual judgement as to whether "It is unlikely a serious developer would copy/paste in crypto code into a project that has real money on the line" is descriptive of the prevalent standard of care in engineering in the Bitcoin community.
Bad advice. Use your OS's CSPRNG to get a seed, but work with your own PRNG (say, HMAC_DRBG) internally. Going to the OS every time you want a few bits is both very slow and makes it far easier for local attackers to see when you're using entropy.
I'll go to bat on this disagreement. Don't use application-layer CSPRNGs. Use the one the OS provides, to the exclusion of alternatives.
They should have disabled OpenSSL's CSPRNG entirely and redirected its internal calls to perform IO operations on /dev/[u]random?
Even that OS with the biased /dev/[u]random using RC4 because it was fast would keep a separate RC4 state in each process' libc to avoid the overhead of kernel calls.
Our crypto libraries need to provide their own belt to wear with the OS-provided suspenders. Yes, I know about potential fork() bugs, but our modern VM-crazed deployments introduce that possibility at kernel level too. We need our good crypto implementers shipping good lightweight CSPRNGs, rather than the kernel developers who try to compensate with 6000 bit entropy pools.
Meanwhile, better to have just one codebase to review, rather than 100 crappy ones, like the SecureRandom from Harmony.
I'm sure these principles of minimal redundancy will be of great comfort to those users who get their private keys exposed.
Do you see the paradox here? Find me these developers who are undoubtedly competent to implement their own CSPRNG, but prefer not to. These are the folks I want implementing the CSPRNG that will be generating my keys, rather than kernel developers who find entropy pools exciting.
We have already seen a huge number of bad keys generated. Is it that obvious at this point that kernel (and embedded system) developers are so much better at this than OpenSSL?
Is it possible that you just tend to look at more broken library and app crypto code during the course of your work? If you worked primarily with broken kernel crypto code, would you perhaps prefer (for your own use) a CSPRNG in a library written by your favorite experts?
Meh, this conversation needed to be over
do random_beverage(); while (self->is_conscious());You'd rather not be in a position to care about cryptographic randomness regardless. So, by all means, use high-level crypto libraries. But those libraries should also be using the random driver, instead of trying to bolt their own CSPRNG on top of it (or, god forbid, trying to avoid the random driver altogether).
Ok, we disagree. It wouldn't be the first time. ;-)
the Debian OpenSSL bug was an entirely unforced error stemming from applications that chose to use Debian OpenSSL's terrible CSPRNG on top of the OS's CSPRNG
I think the biggest problem was that OpenSSL used OpenSSL's CSPRNG after Debian broke it. Having applications all open+read+close /dev/random is not going to prevent that.
a far more likely concrete flaw is, for instance, a SecureRandom implementation that allows developers to specify insecure seed values.
A SecureRandom implementation should not allow the application to provide seed values except as additional input. It should always seed itself from the operating system entropy source, with no option to disable that.
Aside from the issue of performance (which can be severe) and the timing information leakage to local spies, there's a very practical reason to avoid developers read from /dev/random:
int fd;
fd = open("/dev/random", O_RDONLY);
read(fd, buf, buflen);
close(fd);
I've seen this on a number of occasions, and it works perfectly fine... until your server gets busy, the kernel entropy pool runs down, and /dev/random returns a short read. At that point, all hell breaks loose. To me, this scenario alone is enough to tell developers to use a library's automatically seeded CSPRNG instead of reading from the kernel.Oh great, yet another way that Linux developers are going to write broken code because they think the whole unix world behaves like Linux...
There's value in having a uniform interface to all the different OS CSPRNGs.
What I'm saying isn't valuable is duplicative effort to build additional CSPRNG logic in the app layer. As we keep seeing, these app-layer CSPRNGs are additional single points of failure; they don't add defensive depth.
where are you getting your info from?
as far as i can tell previous code was buggy, and android 4.2 "fixed" things by making it impossible (well...) to screw up the PRNG (setSeed in OpenSSL augments state, previously with BouncyCastle it replaced state, afaict).
but that doesn't explain why current software has problems (not the kind of problems that should make it insecure - they may have problems with being unable to recreate keys if they were using seeded PRNG output as keys(!), but 4.2's changes should just screw them completely, rather than make things insecure), or why the article link blames the platform libraries.
so why are you saying this is a problem now in bitcoin library code? is there another link somewhere i've missed? (i agree it's suspicious that everything is bitcoin-related, but i can't see any certain evidence...)
http://armoredbarista.blogspot.com.au/2013/03/randomly-faile...
http://en.wikipedia.org/wiki/Hardware_random_number_generato...
Java implementations primarily used on lightweight mobile platforms have a method called SecureRandom which generates pseudo random numbers for cryptographic operations. The integrated seed generator on some platforms provides a systematic means of determining the seed value and predicting seemingly secure outputs.
http://www.scribd.com/doc/131955288/Randomly-Failed-The-Stat...
For the most part, people who use bitcoin now are still very early adopters and are techies.
Also, IMHO, it would be idiotic to have significant bitcoin value stored on an Android phone, and very few people (if anyone) would do that.
But it seems strange that such a large obvious problem would make it into Android. The other explanation is that Android bitcoin developers are all implementing it incorrectly and either don't realize it or are trying to push the blame somewhere else.
It's really awkward to stumble into as all the evidence points to the framework but when you rtfm you realize no, its really pebkac.
I'm not saying this can't be a vulnerability in the framework, just this is the most likely scenario.
public Random() { this(++seedUniquifier + System.nanoTime()); }
Correct. The standard recommendation is to store significant savings in wallets that you use as little as possible (ideally offline).