Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Bcrypt - symmetric encryption
5 points by uonce on Aug 3, 2011 | hide | past | favorite | 6 comments
I'm investigating whether I can use bcrypt not just for password hashing, but to encrypt database contents as well.

The idea would be to run a strong password through bcrypt (with an appropriately high work factor, so that the operation takes perhaps 10ms), and then use the digest created as a symmetric key to encrypt the database record.

I would obviously NOT store the digest in the database; rather, to authenticate the user, I'd decrypt their database entry and verify that it was correct (something cheap like checking the first few bytes for a known value, for instance).

I would certainly store the work factor and the salt, as those would be needed to re-hash the provided password and generate the symmetric key.

It looks like bcrypt outputs a 186-bit hash (31 base64 chars, and I don't know why it says 184 bits here: http://stackoverflow.com/questions/5881169/storing-a-hashed-password-bcrypt-in-a-database-type-length-of-column).

Not sure if it would it be safe to pad that out and use 192-bit AES as my symmetric algorithm... or is there something insecure about that which I don't realize? Would it make sense to use a different algorithm?

Would love some feedback on whether this scheme makes sense, whether there's something better to do, and any pointers to additional reading.



Stackoverflow is right: the last two bits are just padding, and not at all unpredictable. AES was not designed to work with a partially-known key, so I wouldn't recommend it.

Unlike bcrypt, PBKDF2 was explicitly designed for this purpose, so I'd recommend using that if you do go this route. It'll also get you a strong 256-bit key (in the correct variant). (If you really want to use bcrypt, just truncate at 128 bits and use AES-128, which is good enough. Really.)

Whether using bcrypt or PBKDF2, you'll want to use a salt, which should be stored in the database. Whether using bcrypt or PBKDF2, some users will choose pitifully weak passwords and allow their data to be decrypted (no password hash can save you if you pick "password" or "iloveyou"). You can more-or-less solve this by integrating e.g. http://www.openwall.com/passwdqc/.

All that said, I'm not sure this is a good idea:

- you expose yourself to DDoS by repeated logins, fixable only by setting the work factor for bcrypt/PBKDF2 dangerously low (100 POST "/create_new_account.php" requests per second is not much);

- if your front-end machines are compromised, the attacker can gather passwords from legitimate logins, so you still need to secure your front-end machines;

- encrypting everything that could be useful to an attacker means that you can only use your RDBMS as a networked hash table;

- crypto is hard, and easy to get subtly wrong.

Then again, implementing this would most likely increase your security quite a bit. Just don't rely on it.


Wonderful reply, thank you! Glad to know about PBKDF2. It seems like the right fit.

In this application, I'm actually generating strong passwords and assigning them to users — it's a unique case where that's not a usability concern, and the data is very rarely accessed. My thought with this setup is that I could assign, say, a 64-bit password, which I'd then use to generate longer keys for AES with a purposefully slow algorithm. I'd prefer not to assign users 128-bit AES keys directly because it will be harder for them to deal with the longer string.

In thinking about the code length, I'm trying to form an estimate with rough numbers like so: - I'd like to be able to generate the AES key in 10 microseconds (I lowered that based on your DDoS concern) - I'd like a brute-force attack to take 100 years - An adversary could devote 10,000 times my computing power to cracking (really not sure how to estimate this though... botnets, gpus...)

Given those numbers, my keys need log2(100 years * 10,000 / 10 microseconds) = 62 bits of entropy. So I'd assume 64-bit keys would be secure, if I generate them correctly.

Basically I'm trading off computation time on my server for giving users shorter keys.

I do want to rely on this. Most of the user data should be visible only to the user, although I'll keep some parts (account info) in plaintext or encrypted with a master key. I don't know of a way of avoiding the front-end risk other than making sure to secure it, because the key has to come from the user; only they have it.

Any thoughts on my estimate of the key length and the feasibility of this system?


[Sorry, I haven't forgotten about this message, but things are really hectic right now. I'll try to get to it tomorrow.]


That is so kind! Thank you for this advice. I'll pay it forward online....


Okay, here are my thoughts. The standard disclaimer (I'm actually a mathematician-cryptographer, I just play a security guy on the internet) applies: don't rely too much on me. I hear tptacek does security evaluations, for a fair price. ;-)

Here are some comparisons (because I don't have numbers for cracking speed either):

Block ciphers: 80-bit keys seem to be uncrackable for now (although pretty much everyone recommends 128-bit keys). You're a work factor of 2^80 / 2^64 = 2^16 away from that level of security, but PBKDF2 should be able to supply that (against every attack I can think of, at least). As a very rough guide, my laptop does ~3 million AES (which is not a 80-bit cipher, but should be of comparable speed) invocations on a small buffer per core per second, according to "openssl speed", so you'd aim for 3 million/2^16 ~= 50 crypts per second.

Passwords: passwords with 64 bits of actual entropy (e.g. 12 truly random uppercare/lowercase characters) are extremely rare - the typical entropy of a human-selected password seems to be ~18 bits, which causes security people lots of trouble. OpenBSD's bcrypt takes, again on my laptop and this time timed with a very imprecise 'for i in `jot 1000 0 0`; do encrypt -b6 foo >/dev/null; done', about 7 seconds per 1000 crypts (single-core). (Note that OpenBSD's value hasn't been increased for a long time.) So aiming for 100 crypts per second gets you about the same security as a very good password with a standard login procedure, and 10^5 crypts per second (10 milliseconds per crypt, equivalent to a password with ~54 bits of entropy and a standard login procedure) is still far better than typical for password-based systems.

Something like 50-200 crypts/second probably does allow DDoS attacks via repeated (attempted) logins. On one hand, I wouldn't worry too much: articles like http://www.itbusiness.ca/it/client/en/home/News.asp?id=54907 suggest that you could knock even a sizable dedicated server offline for a week or so on a budget of a thousand bucks, which is probably a more realistic threat (if only because it doesn't require much sophistication). On the other hand, as long as the attacker doesn't gain direct database access but has to go through your front ends, password-based systems seem to mostly work in the real world - rate-limiting helps a lot. (Of course, it also helps that the average Facebook account just isn't very valuable.)

If you do want security from brute-force attacks by attackers who do get access to your database, and if you are unwilling to go to 128-bit keys (which basically solves the issue), consider 80-bit keys: it's three more BASE64 characters, but 80-bit keys alone seem to be sufficient for now (NIST recommends phasing them out in 2015 at the latest, but I know of no real attacks, and there are almost certainly juicier targets). Adding a variable work factor via PBKDF2 makes sense, if only to be able to scale up later without too much hassle. The above 2^16 would get you security sort-of-equivalent to 96-bit keys; something like 2^4 should be essentially instant.

In the end, though, this is a lot of complicated code that you'll have to write (and tptacek would tell you not to do it), and even if you get it right "this guy implemented some nonstandard crypto" will set off some alarm bells.

I think the industry-standard solution is to hand out RSA keyfobs (or the YubiKey, which may end up cheaper once you consider RSA's server-side licensing); do note that RSA recently got completely hacked and handled it very poorly (Google has plenty of details), and that the YubiKey RFID (specifically, the other models are fine) includes a totally broken MIFARE Classic chip (famously used on the "OV-kaart" here in the Netherlands.) Truly paranoid customers may have epoxy'd all their USB ports shut, which would rule out the YubiKey. I don't see how to handle encrypted data with either of these solutions. (Note: I have no idea how well the YubiKey stands up to physical attacks. I'm not sure you care, but...)


Thanks for another great answer. This is a big help.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: