

A possible flaw in open-source bcrypt implementations - lisper
http://rondam.blogspot.com/2011/06/possible-flaw-in-open-source-bcrypt.html

======
daeken
So, he's right in that there seems to be a flaw with respect to the base64
encoding, but he's wrong about how much. It's one byte of hash that's lost
when you add the padding back (it decodes to 184 bits), which is explained by
the following code:

    
    
            encode_base64((u_int8_t *) encrypted + strlen(encrypted), ciphertext,
                4 * BCRYPT_BLOCKS - 1);
    

It does indeed look like it's cutting a byte off the ciphertext when it's
encoded.

Edit: I think I know what's going on with the key length, too. It just so
happens that len(base64.decodestring('x' _72)) == 54 -- just below the key
size limit. But I don't see where the key is getting base64 decoded in the
code. Digging...

Edit #2: The b64d('x'_72) == 4 behavior I saw was coincidental. What happens
is that subkeys can be up to 72 bytes long but will not affect all bits of
ciphertext. This is valid and by design:

    
    
        /* Schneier specifies a maximum key length of 56 bytes.
    	 * This ensures that every key bit affects every cipher
    	 * bit.  However, the subkeys can hold up to 72 bytes.
    	 * Warning: For normal blowfish encryption only 56 bytes
    	 * of the key affect all cipherbits.
    	 */
    

Edit #3: The hash truncation occurs in the reference implementation as well.
This is in disagreement with the paper, and reduces the keyspace by ~4.16% --
that's ungood. Wonder why that was?

~~~
tedunangst
Actually, it reduces the keyspace by 99.6%.

As to why, I've never encountered a software implementation that precisely
matched its design paper. Usually has a lot to do with publication deadlines.
Papers don't change, software does.

~~~
daeken
Sorry, my math was initially off -- the keyspace is reduced _to_ ~99.6%, or a
drop of ~0.4%.

~~~
tedunangst
No, the upper byte of key should have 256 possible values. It now has one (all
zeroes, essentially). Keyspace is reduced to 1/256 of original, a reduction of
255/256.

~~~
daeken
Only for that byte -- the overall keyspace is 184 bits instead of 192 bits.

~~~
esrauch
I think his point is that 2 raised to 192 is 256 times larger than 2 raised to
184, meaning that you are getting 1/256 as many possible keys by reducing the
key length by 8 bits.

~~~
daeken
Ahh, that's a good point. Thanks for clarifying.

------
djmdjm
I'm the py-bcrypt author. py-bcrypt is a thin wrapper around the original
reference implementation of bcrypt from the authors of the paper describing it
(Provos and Mazieres). The discrepancy in hash length is totally harmless
(adding something like 2^-186 additional likelihood of collision) and was
present in the reference implementation. I don't know exactly why the hash is
slightly truncated, but I guess David or Niels thought that 60 character
hashes were a more manageable length.

The author of this assinine blog post only contacted me a few days ago and
obviously couldn't be bothered to wait for a response before proceeding to
imply malicious intent for what is clearly a trivial difference between
academic paper and practical implementation. I hope he retracts it.

~~~
RyanMcGreal
First of all, _thank you_ for py-bcrypt. Thanks to your hard work and
generosity, the tiny sliver of the internet that I manage is a more secure
place.

Now for the request (sigh, you knew it was coming): at my day job, I have to
develop on Windows and it's not feasible to install Visual Studio 2008 Express
on my web server. Is there any other practical way to get py-bcrypt running in
that environment?

~~~
sp332
Maybe you could install just the VS 2008 runtime libraries?
[http://www.microsoft.com/downloads/en/details.aspx?familyid=...](http://www.microsoft.com/downloads/en/details.aspx?familyid=a5c84275-3b97-4ab7-a40d-3802b2af5fc2&displaylang=en)

~~~
RyanMcGreal
Thanks for the suggestion. I tried this, but the install script still returns
the same error: "Unable to find vcvarsall.bat".

~~~
sp332
Hm, that's actually a compiler error. If you can't get the VS compiler
installed easily I think <http://blog.eddsn.com/2010/05/unable-to-find-
vcvarsall-bat/> is your best bet.

------
Ixiaus
Interesting investigation, I would like to see what the response from the py-
bcrypt author is (since py-bcrypt is what I use in my applications).

~~~
llambda
I just started using py-bcrypt for a Flask extension I wrote. I was told
there's a couple of other modules out there that support bcrypt but I was
trying to find a reason to use them over py-bcrypt as it seems to be the de
facto standard. Maybe this is such a reason? iirc cryptacular and bcryptor
were the modules that were recommended to me.

~~~
Ixiaus
I didn't really like cryptacular that much. I don't really know why one
character of the hash is being taken off; I remember curiously wondering why
that was in there when I was fiddling with the source to get it to compile on
Windows.

I also don't know if that would pose as a significant security threat - sure,
you would be taking _one_ character off of the number of characters that need
to be brute forced, but it is only one. I'm not informed enough to give an
accurate opinion.

I do know, though, that jumping to conclusions before a thorough explanation
is provided is silly... Hence why I'm not suddenly jumping to the use of
cryptacular or others.

~~~
StavrosK
I'm guessing it would make brute forcing about 40 times easier.

~~~
daeken
It would make it 256 times easier, reducing the keyspace by ~4.16%.

~~~
StavrosK
I don't see how that can be, given that the character that's chopped off can
take one of 40 values... Or is it case sensitive? Still, nowhere near 256.

~~~
daeken
It's not b64 encoding the final byte of the hash, not dropping a b64
character.

~~~
StavrosK
Ah, I see. You are correct, then. Also, that would make it 64 times, if I were
right, it seems.

------
marshray
I suspect Solar Designer wrote the implementation for John the Ripper with the
goal of cracking passwords, not validating them properly. For this purpose,
slightly-truncated hashes should work just as well (maybe slightly better).

If Openwall and py-bcrypt are using JtR code for actually validating them,
that's a questionable bit of software engineering. JtR may not be doing the
same type of input validation that one would want in your authentication code.
More evidence for this suspicion is that the input length disparity the
blogger Rondam describes.

~~~
djmdjm
No, py-bcrypt uses the reference implementation from OpenBSD.

~~~
marshray
Has anyone looked at the length of the strings on OpenBSD?

~~~
djmdjm
Yes, py-bcrypt produces identical hashes (it was intended to be compatible)

------
neuroelectronic
Very suspicious indeed.

------
ars
It'll be troublesome to fix this since any change will invalidate all existing
passwords.

~~~
jeremyw
Fixed and unfixed versions, no?

I usually include a verbose, independent scheme string next to encrypted db
columns, so a) data is self-documenting for future owners, providing fair
detail to work around forward breakage/compatibility and b) have multiple
methods living in the database during upgrades.

~~~
ars
Does "verbose, independent scheme string" mean:

Have an extra column describing the hashing scheme used for the password?

Isn't that what the $2a$12$ is for?

~~~
jeremyw
No, that minimum is exactly what will fail in this case.

