/* * luascrypt - Lua binding to libscrypt * * Copyright 2015 Daniel Silverstone * * Please see the file COPYING for licence details. */ #include #include #include #include #include #include #include #include "lua.h" #include "lauxlib.h" #include "libscrypt.h" #include "base64.h" static void luascrypt_salt_gen(char *salt, int saltlen) { int fd; /* Following comment applies to libscrypt prior to 1.21: * * We'd go with libscrypt's implementation, but since libscrypt's salt * generation is time based, we cannot fully trust it to generate * unique salts so to improve our chances we assume we have urandom * and fall back to libscrypt's implementation if we don't. Since the * libscrypt implementation is fast, call it, and then overwrite it * if we can... */ libscrypt_salt_gen(salt, saltlen); fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { size_t total = 0; ssize_t n; while (total < saltlen) { n = read(fd, salt + total, saltlen - total); if (n == 0) { break; } if (n == -1) { if (errno == EINTR) { continue; /* just try again */ } /* Ignore all other errors, since we have our fallback. */ break; } total += n; } close(fd); } } static int luascrypt_hash_password(lua_State *L) { // password {N, r, p} -> crypted size_t passwd_len; const char *passwd = luaL_checklstring(L, 1, &passwd_len); char buffer[256]; /* This value is nasty here, but */ char salt[16]; /* while I am not normally into magic */ uint8_t hashbuf[64]; /* numbers, these are taken from the */ char saltbuf[256]; /* libscrypt_hash() source. */ char outbuf[256]; /* Icky, I know, but what can I do? */ size_t bufused; uint32_t N = SCRYPT_N; uint32_t r = SCRYPT_r; uint32_t p = SCRYPT_p; if (lua_gettop(L) > 1) { N = (uint32_t)luaL_checknumber(L, 2); r = (uint32_t)luaL_checknumber(L, 3); p = (uint32_t)luaL_checknumber(L, 4); } /* We know that libscrypt is limited to N of 2^15 or less * so raise an error if N is too large */ if (N > 32768) { return luaL_error(L, "Unable to generate password hash: %s", "N is too large (limited to 2^15)"); } #ifdef TRUST_LIBSCRYPT_SALT_GEN /* Modern versions of libscrypt generate sufficiently random salts * and take a uint8_t * instead of char * */ libscrypt_salt_gen((uint8_t *) salt, sizeof(salt)); #else luascrypt_salt_gen(salt, sizeof(salt)); #endif if (libscrypt_scrypt((uint8_t*)passwd, passwd_len, (uint8_t*)salt, sizeof(salt), N, r, p, hashbuf, sizeof(hashbuf)) < 0) { return luaL_error(L, "Unable to generate password hash: %s", (errno == EFBIG) ? "r and p are too large" : (errno == EINVAL) ? "N is not a power of 2" : (errno == ENOMEM) ? "Buffer sizes are bad" : "Unknown error"); } bufused = base64_encode(outbuf, (char *)hashbuf, sizeof(hashbuf)); outbuf[bufused] = '\0'; bufused = base64_encode(saltbuf, salt, sizeof(salt)); saltbuf[bufused] = '\0'; if (libscrypt_mcf(N, r, p, saltbuf, outbuf, buffer) < 1) { return luaL_error(L, "Unable to mcf encode password."); } /* some versions of libscrypt fail to include the final equals * after mcf encoding -- check and if it's missing, add it back. * known bad: jessie's libscrypt0 package * known good: master of libscrypt as of 8th July 2015 (check date) */ { int oblen = strlen(outbuf); int bulen = strlen(buffer); if (outbuf[oblen-1] == outbuf[oblen-2] && outbuf[oblen-1] == '=' && buffer[bulen-1] == '=' && buffer[bulen-2] != '=') { strcat(buffer, "="); } } lua_pushstring(L, buffer); return 1; } static int luascrypt_verify_password(lua_State *L) { // crypted password -> okbool const char *crypted = luaL_checkstring(L, 1); const char *passwd = luaL_checkstring(L, 2); /* libscrypt_check() mutates the provided crypted data, so copy it * otherwise we damage the memory Lua holds. */ char *crypted_copy = (char *)lua_newuserdata(L, strlen(crypted)+1); strcpy(crypted_copy, crypted); int r = libscrypt_check(crypted_copy, (char *)passwd); if (r < 0) { return luaL_error(L, "Unable to verify password. Bad crypt."); } lua_pushboolean(L, r); return 1; } static const struct luaL_Reg luascrypt_functions[] = { { "hash_password", luascrypt_hash_password }, { "verify_password", luascrypt_verify_password }, { NULL, NULL } }; int luaopen_scrypt(lua_State *L) { #if LUA_VERSION_NUM > 501 lua_newtable(L); luaL_setfuncs(L, luascrypt_functions, 0); #else luaL_register(L, "scrypt", luascrypt_functions); #endif return 1; }