5

I am concerned about the use of bcrypt for client-side password generation. I am developing a password generation function to be used client-side, similar to PwdHash and PasswordMaker.

Much has been said about the advantage of using bcrypt over faster hash functions because it slows down brute force attacks. I know bcrypt uses Blowfish internally, which is a symmetric encryption algorithm rather than a hash algorithm. So there must be a hard-coded key somewhere to use bcrypt, and since Blowfish is being used, it stands to reason that if the key is discovered, the password derivation can be reversed and the original password discovered.

Since client-side code can be decompiled, the key could be easily discovered, making bcrypt unsafe to use client-side. Is my reasoning correct or have I missed something?

Also, in a related question, wouldn't the same argument be valid server-side as well. A hash function cannot be reversed, but an encryption function can be if the key is known. Wouldn't it be safer to use a real hash server side, even if it is faster and therefore more susceptible to brute force attack, than to use bcrypt which is reversible?

EDIT: user10008 notes below (post has been removed) that only parts of Blowfish are used in bcrypt and gave me a link. When I followed a link I found a function prototype that includes key as the last argument. So I still see the key being used to kick-start the bcrypt algorithm. If the key is required, and bcrypt uses symmetrical encryption instead of hashing, isn't the operation reversible?

EDIT: Good answers from both martinstoeckli and user10008. I gave the answer to marginstoeckli because of the last sentence in the response:

BCrypt can be seen as encrypting with throwing away of the key.

This really cleared it up for me. Basically, we go through 2 phases

P -> K ; P,K -> C

and then throw away key K, leaving cyphertext C. Because we throw away the key K, we cannot decrypt back to plaintext P. Throwing away K effectively makes bcrypt a one-way function.

EDIT: From user10008, the steps I gave above are more complex, however the essence is that the key K is used in the final phase and discarded. Thanks user10008.

Ken Clubb
  • 183
  • 1
  • 8
  • Can't hide :). The link was to [wikipedia](https://en.wikipedia.org/wiki/Bcrypt#Algorithm), I'm currently reviewing other implementations. Bcrypt is non-reversible, otherwise there would be no point in using it, but I want to figure out why. – user10008 Aug 24 '14 at 19:02
  • Well, I can see the advantage server-side where the key is not as easily captured. But client-side all is available to hackers. – Ken Clubb Aug 24 '14 at 19:05
  • Its a best practice to only store non-reversible versions of passwords. – user10008 Aug 24 '14 at 19:35
  • Its rather `P -> K; "OrpheanBeholderScryDoubt", K -> C`, or `P -> K; K -> C`, if you add the constant into the function. Also perhaps an explanation to your comment to my answer. – user10008 Aug 25 '14 at 17:45

5 Answers5

7

It's just the other way round, BCrypt does not encrypt the password with a secret key, rather it uses the password as the key to encrypt a known text. In the setup where the key is generated, it uses both salt and the password (variable EksBlowfishSetup.key), to generate a key (variable bcrypt.state) used for encryption.

bcrypt(cost, salt, input)
    state \gets EksBlowfishSetup(cost, salt, input)
    ctext \gets "OrpheanBeholderScryDoubt" //three 64-bit blocks
    repeat (64)
        ctext \gets EncryptECB(state, ctext) //encrypt using standard Blowfish in ECB mode
    return Concatenate(cost, salt, ctext)

EksBlowfishSetup(cost, salt, key)
    state \gets InitState()
    state \gets ExpandKey(state, salt, key)
    repeat (2cost)
        state \gets ExpandKey(state, 0, key)
        state \gets ExpandKey(state, 0, salt)
    return state

BCrypt can be seen as encrypting with throwing away of the key.

martinstoeckli
  • 5,149
  • 2
  • 27
  • 32
4

Bcrypt is not reversible. You can use it client-side as well as server-side.

The key is not static but rather dependent on the password, generated by the function call EksBlowfishSetup(cost, salt, input). The plaintext is known and public, its "OrpheanBeholderScryDoubt". If you wanted to retrieve the key, you would need to mount a known-plaintext attack on 64-times-blowfish, which is very hard, and then you would still only get the key, and not the password.

Wikipedia gives a pseudocode overview of the bcrypt algorithm:

 bcrypt(cost, salt, input)
     state ← EksBlowfishSetup(cost, salt, input)
     ctext ← "OrpheanBeholderScryDoubt" //three 64-bit blocks
     repeat (64)
         ctext ← EncryptECB(state, ctext) //encrypt using standard Blowfish in ECB mode
     return Concatenate(cost, salt, ctext)
user10008
  • 4,315
  • 21
  • 33
  • Just for completeness, if you do eventually get the key, you already have the cipertext, so you can then reverse to get the original plaintext. Right? – Ken Clubb Aug 25 '14 at 17:02
  • what do you mean by "ciphertext"? Everybody knows the plaintext of the 64-times-blowfish. Its the key that matters. When you get the key, you would need to reverse the `EksBlowfishSetup` function in order to get the input. – user10008 Aug 25 '14 at 17:25
2

Edited to add: It turns out that what I've written is correct, but it isn't an answer to the question that was asked. I apologize.

Whatever you send from client to server is the password, whether it has been hashed, sliced, or diced. A password hashed on the client is no more secure than the same string, unhashed. If it is intercepted, it can be used for a replay attack in either case.

The important thing is to be sure the password is transmitted encrypted, e.g. with SSL/TLS.

Have a look at this for a fuller explanation: https://crackstation.net/hashing-security.htm

Bob Brown
  • 5,283
  • 1
  • 19
  • 28
  • 1
    He wants to create a "password generation function", not use it in a process that would otherwise be done with challenge response authentication. – user10008 Aug 24 '14 at 19:42
  • 1
    I read that, but didn't understand it much. Using, _e.g._ BCRYPT will produce a hex string that may or may not be random (have much entropy) depending on what the input was. – Bob Brown Aug 24 '14 at 19:44
  • I guess the input is some combination of a password, and the website name. That the program sends the hash as password to the given website. This approach has some issues, but that's not what the question was about. – user10008 Aug 24 '14 at 19:50
  • I've edited my answer. I sort-of feel compelled to add that without a source of entropy other than a user-selected password, such a scheme doesn't do very much to enhance security, especially as the mechanism is necessarily exposed. – Bob Brown Aug 24 '14 at 20:05
  • non-Exposed mechanisms [shouldn't add to security](https://en.wikipedia.org/wiki/Security_through_obscurity), should they? – user10008 Aug 24 '14 at 22:17
  • Strictly speaking, no. Kerckhoffs's principle / Shannon's maxim tell us "The enemy knows the system." That's why I wrote "especially." If I know a password is, say, 256 bits expressed as hex, I'll have some head scratchin' to do as I plan how to guess it. If, OTOH, I know it's a user-selected password hashed with a known URL using a known algorithm, I'll feed in the top 1,000 passwords from the RockYou breach and guess most such passwords within minutes. That is why there has to be some source of entropy other (and better) than that user-selected password in such a scheme. – Bob Brown Aug 24 '14 at 22:27
  • In my case the only purpose of hashing on the client is to ensure that the derived password coming from the client, produces a generated password that is unique for the given host. In my scenario the original basic password is very strong, so it is not susceptible to brute force attacks. Adding the host name to the end of this strong basic password, then bcrypting to get a derived password that is sent to the host ensures that the same basic strong password can be used across multiple web sites without risk. This is the basis of PwdHash and PasswordMaker. – Ken Clubb Aug 25 '14 at 17:15
0

It's not clear what language you're using, but in the more recent PHP they make this easy by having you use a built-in hash function that can account for the latest and best encryption. There's a verify function to test against the hash. There is also a rehash function that can account for future changes, such that the old hashes can be updated and still verified against. Given the proper implementation, this should take care of most of the issues you might run against. However, as Jeff-Inventor ChromeOS points out, most uses of a hash don't require much more than a simpler algorithm. It depends on your needs and how much you are willing to sacrifice security over processing time/power.

Rob
  • 1
  • 1
    Rob - welcome to security.SE. While nothing in your answer is wrong, it doesn't really answer the OP. The question is about `client-side` password creation while PHP is a server-side language. – Neil Smithline Oct 16 '15 at 19:52
  • 1
    That's true and something I missed. Thank you. I've been educating myself about all of this for a project I'm working on. – Rob Oct 21 '15 at 18:42
0

Common practice is just to use any hash algorithm, such as SHA-2 on the client. For slightly more security, a slow hash can be used such as PBKDF2, bcrypt, or scrypt. A slow hash is a hash designed to require a great deal of computational power, making brute force attacks with lists of common password more diffucult.

The actual security advantage of slow hashes is not large, though, because users select passwords from a fairly small list of common passwords. In my own analysis, about one million passwords (20 bits) recover 50% of accounts (http://www.w3.org/2005/Security/usability-ws/presentations/37-google.pdf). As few as 1000 passwords recover a significant fraction of accounts (6.1%).

It's impossible to hash such a small amount of entropy enough that it becomes a significant hurdle for an attacker.