I need to take a potentially weak user password and derive a strong key for AES encryption in JavaScript. How can I do this?

I expect the hardest problem is generating a good random salt - perhaps an SSL request could be used to ask a server to provide half of the salt. This would work but I'd rather keep it simple if possible.

Should I use bcrypt? Perhaps with however many iterations are necessary to hit about 1 second on slow hardware (current model smartphones).

I only need to support modern browsers, my userbase is small and everyone is a geek.
The derived key will be used to encrypt plaintext passwords with AES, similar to how LastPass works.

If you are using SSL is there a reason you can't trust it to send the password in the clear? If there's a breach and the attacker gets his hands on the salt (or even worse inject his own), you lose the advantage of having a salt.
–
rathJul 22 '13 at 19:29

1

since salts don't need to be secret or unpredictable, only unique generating them shouldn't be a big issue. Apart from the fundamental javascript crypto problems(sending evil code etc.) the biggest practical issue is getting decent performance out of the js implementation.
–
CodesInChaosJul 22 '13 at 19:31

@rath The server will store the AES encrypted data but if someone hacks the server I don't want them to be able to decrypt it. So the decryption needs to be done client side. I won't be deploying the javascript via the same server - it will be a browser plugin installed on the PC by the user.
–
Abhi BeckertJul 22 '13 at 20:29

2

@AbhiBeckert - yes, CodesInChaos is correct regarding the salt... it doesn't have the same stringent requirements as something such as in Initialization Vector (for example). Crypto-JS (link above) also provides functionality for random byte generation.
–
hunterJul 22 '13 at 20:55

3 Answers
3

Okay, for key derivation in the browser you will be using third party libs.

If you want to be the absolute top of the line then scrypt (potential lib to consider) is your best bet with a medium to high work factor based on what your users are going to be using. Bcrypt works but is not memory hard so take that into consideration. (Even 5MB of memory usage could severely impede the usage of custom hardware to attack your keys)

A salt can be derived from the crypto.getRandomValues which takes a typed array and returns it filled with trustworthy random data.

You can't. The best you can do is something like PBKDF2 or scrypt or bcrypt. But they won't generate a strong cryptographic key. If you start with a weak password or weak passphrase, and derive a cryptographic key from it, the result will inevitably be not very strong. Functions like PBKDF2 or scrypt or bcrypt are not a silver bullet. They make things a little less bad than they otherwise would be, but ultimately, it's still bad; it's just less bad than it could be. It's certainly not good, and it's not going to result in a key that I would call strong.

The #1 thing you can do is: don't do that. Don't generate cryptographic keys from passwords or passphrases. Humans are not very good at choosing or remembering high-entropy passwords/passphrases. As a result, given what we know about human behavior, if you derive a cryptographic key from a password/passphrase, your data will be a lot less secure than one would hope.

Instead, if you want strong security, choose a truly random cryptographic key (not one derived from a password/passphrase). Use proper key management. Yes, this is more work. Achieving strong security does require more work. Depending upon your requirements, it's even possible that you might not be able to achieve your security goals within a browser environment. That's life. If you need strong security, the worst thing you can do is give users a false sense of security: make them think they're safe, when actually they have only limited protection.

Asking random visitors to a website to do "proper key management" is impossible. I don't want the key to be stored on my server, I want it to be provided by the user in order for them to log in. That means a password. If they choose a weak password, it's their own fault. I'm not looking for a silver bullet, I'm looking for the best possible solution that fits within the given constraints.
–
Abhi BeckertAug 21 '13 at 23:36

@AbhiBeckert, If you want advice on how to get the best possible security for your web service, I recommend you ask a question that describes your web service, your security needs, your requirements, your user population, and similar information. We can't read your mind; and it's hard to provide a useful answer without that information. As it is, I can only answer the question you asked (whether it's possible to take a weak password and turn it into a strong crypto key); the answer is no, you can't. It doesn't matter how inconvenient that answer is for users; the answer remains correct.
–
D.W.Aug 22 '13 at 0:43

I want the user to provide a password, of whatever strength they feel is appropriate, and have client-side javascript use that as the seed to generate a symmetric AES encryption key. With as much security as is possible, without ever involving anything outside of JavaScript.
–
Abhi BeckertAug 22 '13 at 1:50

The service isn't really important, all that matters is I want nothing except the JavaScript to touch anything.
–
Abhi BeckertAug 22 '13 at 1:52

Feed the weak user PW into PBKDF2-SHA1. There are a few clean and simple js implementations for this - e.g. Parvez Anandam's implementation.
The user can adapt the number of iterations to his liking (warning on values below some min) - about 10000 iterations will give you about 1 sec delay on an average smartphone.

Note that bcrypt implementation in javascript is fairly complex, and scrypt is even more so - to the point that I gave up on it. The PBKDF2 algorithm is good enough and simple to use.

An acceptable salt for the PBKDF2 can be achieved by hashing (user PW || some simple random value).