retiring crypt
The crypt
function is a unix classic. Unfortunately, its age is showing. It’s an interface from another time, out of place on modern systems, and it’s time for OpenBSD to move on.
crypt
char *crypt(const char *password, const char *salt);
The problems with crypt start with the return value, pointing to global static storage. This is obviously not thread safe, but even if it were converted to TLS, the (admittedly unusual) example of cprintf("%s %s\n", crypt(), crypt());
would still be broken.
The interface can be confusing because it’s not clear whether it’s to be used for creating a new hash or checking an existing one. Both! Creating a new hash is awkward because it requires the user to initialize the salt. It’s not just a salt; it’s a strangely formatted underdocumented format string. (The OpenBSD man page perhaps more accurately refers to it as the setting
parameter.) Checking an existing hash is awkward because customary practice is to pass the whole hash as an argument, even though only the setting parts will be used. The caller is still required to call strcmp
on the result.
bcrypt
crypt has lasted this long because the salt/setting parameter has permitted it to be extended over time. In particular, the bcrypt algorithm has enjoyed great success. The bcrypt
function, on the other hand, inherited all of crypt’s idiosyncrasies because it was designed as a drop in replacement. bcrypt has since been used in a variety of applications where unix compatibility is not a concern. To that end, as part of some other work, two new functions were added.
int bcrypt_newhash(const char *password, int rounds, char *hash, size_t hashsize);
int bcrypt_checkpass(const char *password, const char *hash);
They do pretty much what the names imply. They operate entirely on caller provided memory with no global state. Callers no longer need to concern themselves with the particulars of the salt. Errors are returned in a clearly identifiable way, out of band of the hash. (They aren’t documented in the man page yet because they weren’t intended to be the end user interface of choice on OpenBSD and further man page overhauling is coming soon.)
new crypt
To complement the new bcrypt interfaces, there are new crypt interfaces as well.
int crypt_newhash(const char *password, const char *pref, char *hash, size_t hashsize);
int crypt_checkpass(const char *password, const char *hash);
crypt_newhash is a semi-standardized replacement for the pwd_gensalt
function previously found in passwd and a few other utilities, only instead of just generating a salt, it hashes the password as well. In addition to its obvious duties, crypt_checkpass also handles several of the edge cases such as null and blank passwords and hashes, simplifying the calling code even more.
The manpage for crypt_newhash and crypt_checkpass documents these functions.
retiring DES
The original crypt algorithm was DES. It’s kind of old. It’s also now officially deprecated in OpenBSD. login.conf used to allow admins to specify that users’ passwords should be hashed with DES. This was, in fact, the default for YP passwords for compatibility reasons. But it’s 2014 and DES hashes should not be used anywhere, let alone sent over the network. To that end, support for anything other than blowfish hashes was removed from login.conf and associated utilities. crypt
will still hash DES passwords if called directly, but utilities like passwd will no longer permit the creation of new hashes.
code
Most of the relevant code lives somewhere in src/lib/libc/crypt.
revisions
For a brief while, crypt_newhash
took a login_cap_t
argument, an OpenBSD specific type. It’s been changed to take a string.