csquared / fernet.js

Javascript implementation of Fernet symmetric encryption https://github.com/kr/fernet-spec
MIT License
73 stars 29 forks source link

use high quality randomness #4

Closed kr closed 9 years ago

kr commented 10 years ago

Is this reliably random? https://github.com/csquared/fernet.js/blob/190d151/fernet.js#L52

Apparently randomness is the primary difficulty (or impossibility) of doing reliable crypto in javascript. So this is worth extra attention.

http://www.matasano.com/articles/javascript-cryptography/

cc @tmaher @will

tmaher commented 10 years ago

This is insecure. Please don't use CryptoJS for anything, ever.

Specific to the PRNG, they just wrap Math.random which is totally not crypto-grade. See https://code.google.com/p/crypto-js/source/browse/tags/3.1.2/src/core.js#261 This issue has been known for years and they can't even be bothered to fix, even partially. Cryptography in browser-side JS is really hard to get right, and the maintainers don't appear to know what they're doing.

For node, I recommend calling their primitives directly. For in-browser, the only reasonable crypto library I've seen is the Stanford Javascript Crypto Library, which was written and reviewed by a well-regarded crypto research group. Section IV of their white paper discusses the problems with random number generation. Their solution is to seed a real CSPRNG with data from mouse movements. This is actually similar to the Linux kernel's behavior of looking at hardware interrupt timing.

tmaher commented 10 years ago

And just to emphasize Matasano's excellent writeup, there are very few situations where in-browser JS is actually a useful thing. I strongly recommend a giant warning sign in the README around using fernet.js in the browser, including a link to Matasano, and a detailed discussion of limitations and situations in which it might possibly make sense to use fernet in-browser. I'm happy to assist with that part, let me know.

csquared commented 10 years ago

@tmaher @kr i am using sjcl to generate random numbers now

csquared commented 10 years ago

Also, this is only generating an IV, which we include with the text unencrypted

kr commented 10 years ago

Thanks, this makes me feel better at least. (But I'm not a crypto expert.) :dancers:

Also, this is only generating an IV, which we include with the text unencrypted

I'm puzzled by this. There's nothing "only" about generating an IV. The issue here isn't about secret vs public randomness, it's that one must never use the same IV more than once. To achieve that, you absolutely have to use a good RNG.

csquared commented 10 years ago

The way I understand it (and what I think the crypto-js folks are saying) is that the secret, which is generated elsewhere, has to be cryptographically secure, whereas the IV has to be unique, which is a lesser requirement. Maybe even one where Math.random itself would be feasible?

@tmaher is any of that true? or should something more sophisticated be used for IV generation? and do you have some suggestions about a better algorithm? i'm looking for something i can run more or less deterministically? ie: for sjcl the mouse click input could work in browser but not server-side and since the browser is a shit place to do crypto anyways i'd rather optimize for the server side and improve the caveats in the README.

kr commented 10 years ago

Being unique is not a lesser requirement. Math.random is absolutely not good enough for an IV.

Take Tom's suggestion to use only native apis such as window.crypto.getRandomValues and whatever nodejs provides. If fernet is running in a js environment without good randomness, this package should refuse to run.

tmaher commented 10 years ago

If you're optimizing for server-side, absolutely use NodeJS's crypto.RandomBytes(). That wraps OpenSSL's CSPRNG, which uses /dev/urandom to seed.

IVs are complicated, and you're both right. Uniqueness is the key requirement, unpredictability is secondary but also important, and Math.random should be considered harmful. In AES-CBC (what we're using in Fernet), if you're using the same key repeatedly, the IV absolutely should be unique for each message. Failing to do so allows an attacker to spot instances where two encrypted messages have the same first block (and possibly second, if the first block is the same, e.g., contains some stock preamble in every message). We get some small protection here in that the first block contains a timestamp, which while predictable does reduce the likelihood of the attacker being able to just directly compare two blocks of ciphertext to see if they're the same.

There are actually a number of schemes out there where the IV isn't transmitted in clear alongside the ciphertext (called an "explicit IV"), but is instead derived from something else (an "implicit IV"). Earlier versions of IPsec did something like this (I think it was hash(timestamp + packet sequence number) or something like that), as did SSL v3/TLS v1 for messages after the first one. The trend, however, has been to move towards using an random explicit IV, generated with a CSPRNG, like Fernet does. It looks like IPsec did that in 2004 when they added AES support. TLS more dramatically added that in v1.2, and the attacker having prior knowledge of the IV is integral to how the BEAST attack works.

tmaher commented 10 years ago

tl;dr - If you're more of a crypto expert than me, you can use unique-but-not-crypto-random IVs. I don't trust myself to get it right, so I advocate crypto-grade random IVs.

csquared commented 10 years ago

so this is +1 for server-side(?) and right now unknown for browser. i'm trying to see if they at least use the newer browser-exposed crypto functions: https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues

kr commented 10 years ago

Can you call those functions directly?

That seems easier and more reliable.

if (crypto.RandomBytes) {
  ...
} else if (window.crypto.getRandomValues) {
  ...
} else {
  throw ...
}
csquared commented 9 years ago

it has been verified the way i am using the browserify method:

https://github.com/csquared/fernet.js/blob/master/fernet.js#L49

under the hood is calling out to openssl, real browser randomness, or erroring out:

https://github.com/dominictarr/crypto-browserify/blob/master/rng.js

kr commented 9 years ago

Rad :fish_cake: