Anatomy of a pseudorandom number generator – visualising Cryptocat's buggy PRNG
nakedsecurity.sophos.com
nakedsecurity.sophos.com
Cryptocat's hacktivist credibility was cemented in 2012 when
its Canadian developer, Nadim Kobeissi, was stopped at the
US border and interviewed about his Cryptocat-related
programming activities.
s/was/claimed to be/. This software clearly is not Ft. Knox, and its becoming less and less believable that US intelligence would ever feel the need to interrogate the author of an open source project, and with such brittle security, about the cryptography techniques used therein.I find it far more believable that the young developer with a penchant for dramatics and a reply for everything has a talent for PR. And a shallow understanding of the grave consequences of faulty crypto software.
I think they probably saw his tweets "I'M CROSSING THE BORDER NOW OMG I HOPE THEY DON'T GIVE ME TROUBLE FOR BEING A BIGTIME HACKER ACTIVIST", they Googled him, and asked him about his website.
Also I love cats and feel sorry that the name of those lovely animals is used in a discredited entity.
The problem is that your product did not have just a security vulnerability, but had a number of blatantly unprofessional mistakes showing off ignorance and carelessness of its authors.
This is the worst that can happen with an author of open source and especially security software.
I hope at least you the poor cat's devs realize what has happened.
No, not really. No this kind...
Alright I quit.
Mistakes are understandable, however I think in-depth code review and auditing in any environment involving cryptography is an absolute must. Potentially, peoples lives could be jeopardized (either legally or physically) if they believed their communications were secure, when in fact they were not.
However, the off-by-one bias was actually the least of the problems with Cryptocat's random numbers...
From reading Steve's write-up, the problem was that their keys were ridiculous undersized, because they called their own function wrong:
May 7, 2012 (switched from DH to ECC):
myPrivateKey = Cryptocat.randomString(32, 0, 0, 1);
April 19, 2013: rand = Cryptocat.randomString(32, 0, 0, 1);
myPrivateKey = BigInt.str2bigInt(rand, 10);
June 3, 2013: rand = Cryptocat.randomString(64, 0, 0, 1, 0);
myPrivateKey = BigInt.str2bigInt(rand, 16);
> The bug that lasted 347 days was the confusion between a string and an array of integers. This made the ECC private keys ridiculously small because they passed a string of decimal digits into a function expecting an array of 17, 15 bit integers. Each character was considered an element in the array. So each of those "15 bit integers" were only the values 0 to 9 (3.32 bits). Also the least significant 3 bits are zeroed giving you a key space of 2*10^16 (2^54.15). -- http://tobtu.com/decryptocat.phpSee for yourself here: https://github.com/cryptocat/cryptocat/commit/a17bb1599463e0...
Even now looking at this code, for me it just doesn't pass the 'WTFs per minute' test... It still looks fucking wrong to me... Cryptocat.randomString(64, 0, 0, 1, 0) returns a 64 character string of 0-9, why are they calling str2bigInt with a hex radix? Did they add the last '0' in the wrong place? Or maybe I'm looking at the wrong check in...
EDIT: Ok, it WAS still wrong as of June 4th... It wasn't until July 4th until it finally became:
var rand = Cryptocat.randomString(64, 0, 0, 0, 1)
See: https://github.com/cryptocat/cryptocat/commit/8fb7f4b8e59c76...EDIT EDIT: So just to be clear... v2.1.11 which was supposed to fix this problem has this 'incorrect radix' bug in it? Please tell me I'm wrong, or do we need another CVE and another release?
Pointing this out would be be gratuitous, except that Cryptocat's project leader has very high standards when it comes to the disclosure behavior of that project's competitors:
https://github.com/SilentCircle/silent-phone-base/issues/5
(Cryptocat is "kaepora" in that thread; the context there is that Mark Dowd had reviewed ZRTPCPP, an open source implementation of the ZRTP protocol that Silent Circle funds and relies on, and found several memory corruption vulnerabilities. Mark had gone public with those vulnerabilities quickly as a result of a miscommunication with the Silent Circle team, but then redacted his disclosure and agreed to embargo the findings while Silent Circle coordinated fixes with all of the Silent Circle competitors that relied on that library; what you see in that thread is, apparently, the lead developer of Cryptocat, which doesn't use ZRTPCPP, repeatedly criticizing Silent Circle's handling of the incident.)
The project should meet the standards that it has loudly set for others, and I don't think it has. I'd be happy to be corrected on this.
In the process of handling an embargoed disclosure, Silent Circle groomed (perhaps aggressively) a Github pull request thread. Handling a coordinated disclosure on a Github pull request is tricky under the best of circumstances, but the Silent Circle team was dealing with an unplanned partial disclosure and was, with Dowd's cooperation, trying to get the genie back into the bottle long enough for (from what I can tell) their competitors to get patched.
This was, as is evident from the thread, a problem for Cryptocat's uninvolved, un-implicated lead developer.
Meanwhile: I cannot immediately tell from the record which of the independently discovered vulnerabilities in Cryptocat correspond to which CVEs or which advisories and which severity descriptions.
The problem isn't one of demeanor; it's that I can't tell what's going on with this project. Does this sound fiddly? Let's make it more concrete: did an exploitable vulnerability get reported to the Cryptocat project, mitigated in a public Github commit, and sit there for months without any acknowledgement from the project?
Cryptocat's project leader is an HN commenter, and if this confusion is my own doing --- totally possible --- I'd be happy for some clarification; not about Silent Circle, but on how someone would get a record of what vulnerabilities were disclosed in Cryptocat, when, and what the timeline was for each from discovery to public disclosure. It's plausible that there's a clear record somewhere and I just missed it.
How do you go back to the same mission-critical single line of code again, and AGAIN and keep getting it wrong?
Any change to that line of code raises extreme red flags. As a developer, if you're making a change to that line after it shipped, you're in complete "wow I fucked up bad" mode. Instead it looks like they're just changing it randomly, month after month, like it's some inconsequential debug flag. Really, this just blows my mind.
But I am game developer, and game developers (specially RPG fans) love random numbers.
Some games of mine, I suspected something was off with the PRNG, and did something like they did on the ending, I used the random number generator to draw pictures.
Biased generators were quite obvious, because they made obvious patterns (one of the worst offenders was C default random function on the mingw that came with Dev-Cpp, instead of drawing noise, or weird noise, it drew diagonal lines... yes, LINES, it became clear to me that it was terrible...)
In the end I choose Mersenne Twister, it is a very good PRNG... For games that is, although the Mersenne Twister does generate random looking numbers, its author claim it is bad for crypto, Mersenne Twister, I don't remember why, can be predictable enough for anti-crypto.
Because after observing a certain number of outputs, you can completely predict its subsequent outputs.
If it's too uniform, sample from other distributions, but to do so usually requires taking output from a PRNG.
Indeed using the inverse CDF of any distribution is a map from (0,1) to the desired sample space, following the distribution.
As for which PRNG for RPG games, MT is very good because while it isn't cryptographically secure, it otherwise has a very long period and delivers very nicely distributed numbers as well as being very fast.
For non-crypto applications such as monte-carlo, there's little reason to use much else.
Alternatively, some sets of random values don't look random to humans, so as a game developer you might need to tweak it and make it appear more random to the player. e.g. these are some truly random bits from Hotbits (not a PRNG: https://www.fourmilab.ch/hotbits/secure_generate.html): '001101010011001011001000010100011010011111000001111101010011000111010000111000001011111000111011'. My pattern-matching human brain is annoyed by runs longer than 3 or 4 times, and patterns like 000111 and 111000. If you take time to filter out 'annoyances' you can make the player like your game more, and if you're careful you can do it without decreasing the entropy bits per byte too much.
But could someone explain how an attacker can take advantage of the fact that 0 is returned ~1% more often than other digits? It this flaw alone sufficient to break cryptocat? Or does it simply make brute forcing easier when combined with other crypto flaws?
That's (potentially dangerously) oversimplified. Uniformity is one of the consequences rather than the requirement itself. The core requirement is unpredictability. If the output is non-uniform, it means the output isn't fully unpredictable (which may or may not lead to practical attacks depending on the degree of the problem and how the PRNG is actually being used).
Meanwhile, you can also create a uniform PRNG that isn't secure at all (e.g. http://en.wikipedia.org/wiki/Mersenne_twister ).
Unfortunately, the sort of nuclear power plant operators which tolerate oil spills are often sufficiently not on their A game to tolerate sparks.
This is a very handwavy explanation. In particular, God doesn't hate nuclear powerplants and try to introduce sparks into them at inopportune moments just to see if they happen to find an oil spill, but The Adversary often can and will do this to your cryptosystem.
The essence is that in modern cryptography you're creating a provably secure system. One of those proofs is that your prng outputs a uniform distribution.
[0] http://www.daemonology.net/blog/2013-06-17-crypto-science-no...
The recent work on biases in RC4 contains a good example of how small biases can end up being exploitable. http://www.isg.rhul.ac.uk/tls/
Suppose you are using the PRNG to make a stream cipher. Basically, your random key is a seed for the PRNG. You then generate lots of pseudo-random characters from that seed, and XOR them together (character-by-character) with your message to encrypt it.
Now, the fact that XOR is linear (it's just addition mod 2) means when you XOR two probability distributions against a constant (i.e., an atomic distribution), you'll get a shifted distribution. Let's say your PRNG disproportionately outputs "0" at each character. Then the distribution of each character of the ciphertext will be centered at p XOR 0, where p is the corresponding character of the plaintext!
So by the law of large numbers, if we see the same message encrypted many times, we can determine with high probability exactly what each character of the plaintext is, and completely break encryption!
Using other encryption methods the advantage is often not so obvious, but it exists. Also it should be noted that 1/250 is a pretty large bias and would probably not even need particularly many ciphertexts.
One of the things I built out of Java when I was hanging out on the cypherpunks list was a 'Noise Sphere' applet. Basically this is a way of testing a PRNG visually. It was fun to put various ideas through it to see how they panned out (most really sucked) One of the cool things was using the alpha emitter RNG from a smart card and finding out that it too was slightly biased in the presence of foil on one side (never did find out why that was, but it did stick out)
The visualization (of the encrypted data, not of any PRNG used) showed that the encrypted data had strong patterns. The data wasn't encrypted using RSA, it used XOR with a non-changing cipher block. (This is trivially easy to crack. Even I could do it.)
The error arose because the manufacturer of the controller chip was using confusing terminology.
> The IM7206 merely uses AES encryption when saving the RFID chip's ID in the controller's flash memory. The company explained that actual data encryption is based on a proprietary algorithm.
(http://www.h-online.com/security/features/Enclosed-but-not-e...)
Other people interested in testing PRNGs might be interested in the DIEHARD tests (http://en.wikipedia.org/wiki/Diehard_tests), but read all the cautions too.
People with no programming experience should be literally banned by law from writing critical software. You should take those bugs way more seriously.
PS: I have seen a programmer's face fear when asked to write crypto software. They know enough to be shit scared. You need that kind of people.
Even unrelated code is heavily audited to make sure that it can't somehow impact the main control loop and cause an invalid state.
Cryptography software should be much the same.
Formal verification of software properties is an interesting field.
I assume that the crypto group I know, who developed most of the code generator I use, takes similar measures to make sure that their verified "theorems" translate correctly to code.
The unverified stage is actually the hardware, which is an open problem.
What industry is this?
Our low level code (ie, directly controlling machines) is written in the SPARK environment. This code tends not to get updated often, and has a high level of verification to it. It's what actually handles the pressure cut-offs (ie, hard limits on the machine), turning vales, etc.
Our middleware code is based on a specially developed VM that gets code generated for it in Coq, to ensure that it doesn't choke or become unresponsive. However, it's not directly responsible for safety control and has somewhat laxer restrictions.
Coq is useful for demonstrating that the middleware analytics will complete in a given time profile and not crash out the server on erroneous input.
Getting the government involved in who gets to write crypto software...
What could possibly go wrong?
This is, of course, very difficult to implement in practice.
However, my guess would be that with proper processes, you should be able to let a junior programmer write the code, because bugs and errors and mistakes will come out in the wash. It's not like experienced programmers don't make mistakes! Perhaps it simple doesn't make sense to turn a junior guy loose, but my thinking is that where people's lives are at stake, depending on someone being a 'good coder' is a bad idea.
Most of the time, generalist developers can severely (sometimes even completely) mitigate the expense of competent design and verification by adopting trusted components and adapting the application requirements to those components (instead of the other way around, which is the usual way developers incorporate third party components).
If you don't do that, though, you're looking at the 10x-1x-10x problem: however much time it takes you to build your system, you're looking at a 1:20 ratio of effort for non-implementation work, and that's serialized.
(Lest anyone thing I'm talking my own book here: we do what I think is an atypically good job at handling the ancillary crypto stuff that comes up in normal applications, but I don't think we're well qualified to do formal reviews of cryptosystems.)
I guess next time I'll be ready to say "that's not something you do during an interview."
I am thinking of a disclaimer like "We believe our product is secure enough that the minimum cost for to an adversary to decrypt a message is at least $ 10^k" for some k and the cost is an estimate of the total cost of factors like number of hours of cryptanalysis, number of cycles, etc.
This way, if the code is found to be completely broken at some point then the error is considered relative to the level of security the designers intended.
Makes it a lousy, toxic topic for message boards but that seems a comparatively small price for a defense against the notion that Cryptocat is (or likely, ever will be) secure.
repeat
byte250 = randomSalsaByte()
until byte250 <= 250
I am not sure what sort of code-review process they had in place if someone saw that and thought "Yup, brilliant. Ship it." I am sure we have all written code like that at one point in our lives, but code that is shipping to the general public?https://github.com/cryptocat/cryptocat/blob/master/src/core/...
As far as I know, it is the only solution for generating unbiased random numbers in a range that does not divide the native range of the PRNG you're using.
What I don't understand is why they're working in decimal in the first place. The obvious solution is to generate a random integer in the range [0, 2^53) (which does not require an unbounded-time loop, since 2^53 divides 2^64) and then divide by 2^53. I am open to the possibility that there's some quirk of floating point arithmetic that means that won't work [edit: less so now that I see it's how Java implements it¹], but I can hardly imagine that it's worse than this crazy business of generating 16 decimal digits because, hey, log₁₀(2^53) is pretty close to 16.
¹http://docs.oracle.com/javase/7/docs/api/java/util/Random.ht...
Almost all crypto operates in the realm of absurdly close to certain.
This is the standard method for selecting a uniform range from a source that is not an exact multiple. The worst-case probability of having to draw again can always be kept under 0.5. For example, I want a number in the range of (0 <= n < 129).
So even for simulation you can have rather subtle effects, even though there you get bitten just by bad luck. The task for cryptographically secure random number generators is actually quite a bit harder, because a attacker tries to exploit any weakness in your RNG, but your tests do only tell you which specific attacks do not work.
Historical C standard library implementations did tend to use the same or similar LCRNGs, but that was never a requirement, and is no longer the case.
Designing a robust CSPRNG is as hard a problem as designing a block cipher mode or a MAC; it's "systemsier" than designing your own block cipher core, but about on par with coming up with a simple authenticated encryption mode. By way of example, CSPRNG design gets extensive treatment in its own chapter in Schneier and Furgusen's _Practical Cryptography_, and it's one of the few chapters that highlights the authors own work.
Cryptocat uses JS inside of a Chrome extension to avoid the chicken/egg secure load problem (I'm ambivalent about this approach). But Chrome extension JS doesn't avoid the other problems of browser Javascript crypto, and here's an example of why: it forced Cryptocat to attempt the design of a custom CSPRNG.
I would be more interested to know if this is sufficient to break Cryptocat. Would this really make a brute force attempt much easier?
As for the "endemic blind spots," any crypto software needs professional reviews and a good deal of time before it can be used in critical situations.
http://blogs.marketwatch.com/thetell/2013/06/06/nsa-targets-...
If they are targeting non-crypto people (which includes me), explaining how a bad random algorithm affects cryptology would be better instead of showing that algorithm is bad in 5 different ways.
Also any link to articles about that Debian bug?
http://www.schneier.com/blog/archives/2008/05/random_number_... http://research.swtch.com/openssl
Errors include:
i) using a source that is not random. As mentioned elsewhere some hardware devices provide skewed numbers, and even de-skewing doesn't help too much.
ii) using a poor seed.
The Debian bug is an example of ii -
> This vulnerability was caused by the removal of two lines of code from the original version of the OpenSSL library. These lines were used to gather some entropy data by the library, needed to seed the PRNG used to create private keys, on which the secure connections are based. Without this entropy, the only dynamic data used was the PID of the software. Under Linux the PID can be a number between 1 and 32,768, that is a too small range of values if used to seed the PRNG and will cause the generation of predictable numbers. Therefore any key generated can be predictable, with only 32,767 possible keys for a given architecture and key length, and the secrecy of the network connections created with those keys is fully compromised.
(http://en.wikinews.org/wiki/Predictable_random_number_genera...)
(www.schneier.com/blog/archives/2008/05/random_number_b.html)
Anyway, I really enjoyed this article. Informative, interesting, and free of snark.