For those interested it uses crypto-js (http://code.google.com/p/crypto-js/).
Might want to increase the random entropy source, from what I can see it's just DateTime.
Also I'd use a better hashing algorithm than SHA1 with AES-256.
I understand the need for PBDKF2, but I'm not so sure about using the password as the key and salt.
That said: this is an idiosyncratic (rekeying AES every 500ms?) and not particularly resilient cryptosystem. As a commenter said below: don't use this for anything serious. You're equally well off with a server that simply sends "plaintext" messages over TLS.
Reasoning about security systems (click my name to see that I do that for a living) is frustrating. To see what's happening in this case, steel yourself for a headache, sit down, and think hard through all the different things that attackers can potentially influence. Then ask yourself, "what could an attacker accomplish by manipulating that thing?".
<!-- cryptocat uses the crypto-js library - http://code.google.com/p/crypto-js/ -->
<!-- http://crypto-js.googlecode.com/files/2.3.0-crypto-sha1-hmac... -->
The browser JS runtime does not provide the tools you need to verify the integrity of a cryptosystem. Full stop.
Actually, browser JS runtimes don't even provide the tools you need to safely run a cryptosystem. How do you know what intermediate values are cached by any given JS? How do you know if any given crypto function is leaving footprints in browser memory? You don't; nobody does; nobody has published the exhaustive analysis of any browser JS to support an argument either way.
Not that that matters for crypto.cat; they don't seem to care much about side channel stuff; for instance, their HMAC compare is the JS string "!=" operator.
As for "applies to so many other servers": I don't know what you're saying. You made a claim about the security properties of this application. That claim was wrong. It doesn't become right when you point out some other app that has the same flaw.
But people hate Java, and it's increasingly disabled in browsers.
I understand that one cannot trust the JS crypto for handling the private key and doing any singing & encryption because of the possibility of leaking information through side channels. That's why I was wondering if it'd be possible to use JS crypto in only one way — that is to verify messages against public key received through the secure channel.
If the JS crypto only does verification, it wouldn't matter if the _public_ key is cached, as long as it can't be arbitrarily changed by another process. If the server is taken over or there's an XSS or other client-side vulnerability the bidirectional TLS would be similarly useless anyway.
† There are issues that damage clientside JS crypto that don't blow up the app itself, but they are not the core issues I'm concerning myself with here.
Unfortunately I can't just bootstrap the application over secure channel and fetch any content over not secure channels, as it'd be trivial to then poison the application using the unencrypted communication. Take a Hacker News-like application: Let's say that news and comments can be accessed over unsecure channel and voting and authentication are performed over TLS. Even though the authorized operations are safe, the data could be poisoned over unsecure channel — for example the user-id could be manipulated and the user would end up voting securely for someone else's post. To mitigate it, all data would have to be fetched over secure channel. However currently this is all or nothing, by fetching public data over TLS, one has to pay the encryption tax — in both CPU time, as well as in disabling any caching middleware.
Of course singing HTTP messages wouldn't be just TLS-light, since the messages could be stored and kept as an evidence that the webserver has produced such a message, which is different to TLS session-based guarantees, but it should be fine for what I need. This is why I was interested to know if such an approach would not degrade the security of the web application below what bi-directional TLS could offer.
I suppose this is not the most exciting use for client-side cryptography right now…
I think the web-application are increasingly going in the direction of reinventing Java applets anyway. There are few components that are needed for that and that will be gradually introduced: 1) Application that can be installed and launched locally (e.g. more sophisticated version of Chrome Webappstore). Once you have that, you basically have RPM in browser. 2) Capabilities system that controls what and how an application can access. All the pieces are already in there: web extensions, mobile apps, etc. 3) Pluggable storage to eliminate the need for storing data on the remote server. There is LocalStorage and IndexedDB already, now it's just a matter of time till Chrome adds Google Storage, Apple adds iCloud, Ubuntu adds UbuntuOne etc.
Once you have that, the Java[Script] applets are basically back. With NaCL in the mix you could even actually run the JVM in the browser once again. So yeah, I think in the long run the client cryptography will come around. But for now I just wanted to make use of what's currently available.
The idea of replacing it with ad-hoc encryption at the application layer carries with it the notion that the basic problem of "taking some bundle of application data and securing it so that it is encrypted in flight" is somehow simple, and that TLS 1.2 is overkill for it. But that's not true. To a significant extent, everything TLS 1.2 does that you don't do is a vulnerability in your system. You won't see it or fully understand it; extremely competent people have gone years staring terrible vulnerabilities in TLS in the face, even after those flaws were pointed out to them, and let them pass because the real-world practical impact wasn't obvious at the time.
And this is all before you get to the point where, to avoid the overhead and complexity and inflexibility (and whatnot) of TLS 1.2, you choose to build your cryptosystem in the most hostile possible environment for cryptography: the language runtime where no operation, function, or binding can be trusted, where every symbol table is potentially corrupted by any of 10s of content sources, many of them under the control of attackers, in a runtime that is being aggressively redesigned, optimized, JIT'd, cached, and transformed for performance.
So you can see that this goal puts you out in the wilderness on two vectors: one of attempting to start from scratch on secure transport protocols, when 20 years has shown us that the simplest service models for secure transport are surprisingly hard; and the other vector being "trying to implement secure crypto in a language environment that's hostile to the concept".
Skip it. Work on something that will make people's lives better in the immediacy, instead.
Yes, I understand that this would be a terrible idea. That is why I only intended to verify signed, public content using static key. I don't indent do any encryption in client-side JS, not even to generate session keys!