What I did was simple: I wrote a well-documented header file with a minimal set of functions, inspired by one of the Python implementations, and a single implementation file that, combined with Solar Designer’s code, generates a static library you can link into your programs. Later, I fixed some stupid coding mistakes I made, despite its small size, and forgot about it.
The project, as of the time I’m writing this, has 95 stars and 35 forks in GitHub (not many, but more than others) and not long ago I realized it’s one of the first Google search results when trying to find a “bcrypt library”. So it seems my small experiment has been promoted and I have to answer to a social contract!
In the last couple of weeks, I’ve been working a few minutes almost every day polishing the library, improving its documentation, reading other people’s code and documentation and adding some functionality. You can take a look at the results in the project’s future branch. Summary of changes from master:
The main implementation has been changed from a static to a dynamic library so it’s easier to update the implementation if a problem is found, without recompiling everything. I use -fvisibility=hidden to hide internal symbols and speed up link time. A static library is also provided, just in case you need it.
The function to generate salts has been changed from using /dev/urandom to using getentropy. That means the library will probably only compile under a modern Linux and maybe OpenBSD, and this is the main reason these changes are still not merged to the master branch. Without despising the BSDs at all, let’s be practical: Linux is the most widely used Unix-like system for servers and getentropy, introduced by OpenBSD, is just better than /dev/urandom because it’s simpler and safer to use, can be used in a chroot, etc. With Linux now implementing it, there are not many reasons to use anything else.
I have added a manpage documenting everything better and emphasizing the 72-byte implementation limit in password length.
I have added functions and documentation explaining the rationale for pre-hashing passwords before sending them to bcrypt, which works around the previous limitation in part.
There’s now an install target.
I have added a pkg-config file to ease finding out compilation and linkage flags in the installed library.
Tests have been separated to their own file and made a bit more practical.
I’ll merge these changes to master after things calm down for a while, I process any feedback I receive (if any) and after Red Hat, Debian and Ubuntu have a stable or long-term support version with glibc >= 2.25, which introduced getentropy. The next Ubuntu LTS will have it, the next Debian stable will have it and RHEL 8 will probably have it.
I may also try to package the library for Fedora, which eventually should make it land in Red Hat. I’m not a Fedora packager yet and this may be a good use case to try to become one and learn about the process.
If anyone’s interested in the project, please take a look at the future branch, comment here, open issues in GitHub, mail me, etc. Any feedback is appreciated because this was just a small experiment and I’m not a user of my own library. Also, I don’t recall ever publishing a shared library before. If I’m doing something wrong and you have experience with that, feedback is appreciated too.
What about Argon2?
Argon2 is another password hashing algorithm that won the Password Hashing Competition in 2015. The competition tried to find an algorithm that was better than bcrypt and scrypt. Its official implementation is released under the terms of the CC0 license, it works under Linux and many other platforms, and builds a shared and static library featuring both high-level and low-level functions. In other words, Argon2 already has a pretty good official, easy-to-use, API and implementation.
In my opinion, if you want to use Argon2, you should be using its official library or libsodium. The latter has packages for most distributions and systems. Argon2 is the best option if you want to move away from bcrypt, but there is no need to do it as of the time I’m writing this. The benefits are mostly mathematical and theoretical. Argon2 is much better, but bcrypt is still very secure.
Argon2 has three parameters allowing you to control the amount of time, memory and threads used to hash the password, as well as a command-line tool to experiment and find out the best values for those parameters in your use case. The libsodium documentation also has a guide to help you choose the right values.
The official library only contains the password hashing functions and leaves out details like generating a random salt or pre-hashing the password to harmonize password processing time. A few quick tests done with the argon2 command line tool from the official implementation revealed a small, almost insignificant password processing time difference between a small 5-byte password and a large 1MB one. I conclude Argon2 doesn’t need pre-hashing but I don’t know the underlying details. You can also choose the size of the generated password hash.
If you’re using libsodium, it includes several functions to generate high quality random bytes, needed for salts. A plain call to getentropy in Linux should also be trivial.
What about scrypt?
If you’re currently using scrypt you already have an implementation, and if you’re not using it yet but you’re considering using it in the future, you could skip it and jump straight to Argon2 (see the previous section). It’s a better option, in my opinion.
If you insist on using scrypt, there’s already a libscrypt project that’s packaged for Debian, Fedora, Arch Linux and others. It takes most of its code from the official scrypt repository to create a library separated from the official command-line tool.
The library also covers obtaining random data to generate seeds (it uses /dev/urandom), but not password pre-hashing. As with Argon2, a small test with a custom command-line tool revealed a very small and almost insignificant password processing time difference between a small and a very large password, so I conclude again no password pre-hashing is needed for libscrypt.