I’ve being doing it wrong for years. Till 5.3, PHP didn’t help very much either with it’s broken bcrypt implementation. So here goes the vicious circle. However, there’s no excuse for getting smarter as the tools I’m using get smarter. Here’s a couple of reasons.
For quite a while I’ve being using my own implementation of phpass. There are a couple of reasons for doing my own homework instead of using the already existing solution:
- The Suhosin patch doesn’t play nice with PHP extensions available as binary blob only as it breaks the ABI. Therefore a consistent bcrypt implementation lacked before PHP 5.3 since at most I could use just the Suhosin extension.
- The random seed generator was quite broken across different platforms. Unices have /dev/random and /dev/urandom. Even more, these devices are not consistently implemented across them. If you aren’t inspired (/dev/random under Linux is a blocking device), then your random seed generator is screwed till the system gains enough entropy. Under Windows, the used PRNG was something really crappy, if you could use the system implementation, otherwise people used to hack something together. phpass is still a mess, while being backwards compatible with a lot of PHP implementations.
openssl_random_pseudo_bytes() is still slow under Windows, although since PHP 5.3.4 it fixes the blocking behavior. But it’s still better than the previous mess. My method for generating the bcrypt salts is still about 5.8X faster than the method used by phpass under Ubuntu.
I am not saying that my implementation is the implementation, but I got the feeling that for once I am doing it better that I used to do it before, or doing it better than phpass does.
Grab/fork it from GitHub if you’re curious.
Update: I also implemented Ulrich Drepper’s SHA2-based crypt() scheme, but the v0.2 is incompatible with v0.1 due to the fact that it returns the hashes as base64 string. This is a side effect of using 16 bytes of random seed for the SHA2 salts. The PHP manual simply states 16 chars, but that’s a little bit vague. Except chr(0) and chr(36), any other single byte char is valid for the random seed. bcrypt takes a more cleaner approach for the 16 bytes random seed by using a 22 chars base64-like string that encodes those 16 bytes.