From 9affdc735bc5731580619057405d16cf588afe9b Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Wed, 8 Jul 2015 19:54:40 +0100 Subject: Change to using libscrypt even though it has known failure modes --- luascrypt.c | 271 ++++++++++++++++++++++++------------------------------------ 1 file changed, 106 insertions(+), 165 deletions(-) (limited to 'luascrypt.c') diff --git a/luascrypt.c b/luascrypt.c index f863381..2492cf7 100644 --- a/luascrypt.c +++ b/luascrypt.c @@ -1,208 +1,149 @@ -/* scrypt for Lua - * Copyright 2013 Rob Kendrick - * - * Heavy based on Barry Steyn's code, and distributed under the same licence: - * - * Copyright (C) 2012 Barry Steyn (http://doctrina.org/Scrypt-Authentication-For-Node.html) - * - * This source code is provided 'as-is', without any express or implied - * warranty. In no event will the author be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this source code must not be misrepresented; you must not - * claim that you wrote the original source code. If you use this source code - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original source code. +/* + * luascrypt - Lua binding to libscrypt * - * 3. This notice may not be removed or altered from any source distribution. + * Copyright 2015 Daniel Silverstone * - * Barry Steyn barry.steyn@gmail.com + * Please see the file COPYING for licence details. */ -#include -#include +#include #include +#include + +#include +#include #include #include -#include -#include -#include -#include +#include "lua.h" +#include "lauxlib.h" -#include "sha256.h" -#include "sysendian.h" -#include "crypto_scrypt.h" -#include "memlimit.h" -#include "scryptenc_cpuperf.h" +#include "libscrypt.h" -/* - * Obtains salt for password hash. This function is copied from Colin Percival's scrypt reference code - */ -static int -getsalt(uint8_t salt[32]) { +static void +luascrypt_salt_gen(char *salt, int saltlen) +{ int fd; - ssize_t lenread; - uint8_t * buf = salt; - size_t buflen = 32; - - /* Open /dev/urandom. */ - if ((fd = open("/dev/urandom", O_RDONLY)) == -1) - goto err0; - - /* Read bytes until we have filled the buffer. */ - while (buflen > 0) { - if ((lenread = read(fd, buf, buflen)) == -1) - goto err1; - - /* The random device should never EOF. */ - if (lenread == 0) - goto err1; - - /* We're partly done. */ - buf += lenread; - buflen -= lenread; - } - - /* Close the device. */ - while (close(fd) == -1) { - if (errno != EINTR) - goto err0; + /* 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) { + read(fd, salt, saltlen); /* Ignore errors in these two calls */ + close(fd); /* Since we have our fallback. */ } - - /* Success! */ - return (0); - -err1: - close(fd); -err0: - /* Failure! */ - return (4); } static int -ls_hash_password(lua_State *L) +luascrypt_hash_password(lua_State *L) { - const uint8_t *passwd = (const uint8_t *)luaL_checkstring(L, 1); - size_t passwdlen = lua_objlen(L, 1); - uint64_t N = luaL_checknumber(L, 2); - uint32_t r = luaL_checknumber(L, 3); - uint32_t p = luaL_checknumber(L, 4); - - uint8_t dk[64], salt[32], hbuf[32], header[96]; - uint8_t *key_hmac = &dk[32]; - - SHA256_CTX ctx; - HMAC_SHA256_CTX hctx; - - int result; - - errno = 0; - if ((result = getsalt(salt)) != 0) { - lua_pushstring(L, strerror(errno)); - lua_error(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? */ + 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); } - errno = 0; - if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64) != 0) { - lua_pushstring(L, strerror(errno)); - lua_error(L); + /* 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)"); } - memcpy(header, "scrypt", 6); - header[6] = 0; - header[7] = (int)log2(N); - be32enc(&header[8], r); - be32enc(&header[12], p); - memcpy(&header[16], salt, 32); + luascrypt_salt_gen(salt, sizeof(salt)); + + 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"); + } - SHA256_Init(&ctx); - SHA256_Update(&ctx, header, 48); - SHA256_Final(hbuf, &ctx); - memcpy(&header[48], hbuf, 16); + if (libscrypt_b64_encode(outbuf, (char *)hashbuf, sizeof(hashbuf)) < 0) { + return luaL_error(L, "Unable to encode password hash."); + } - HMAC_SHA256_Init(&hctx, key_hmac, 32); - HMAC_SHA256_Update(&hctx, header, 64); - HMAC_SHA256_Final(hbuf, &hctx); - memcpy(&header[64], hbuf, 32); + if (libscrypt_b64_encode(saltbuf, salt, sizeof(salt)) < 0) { + return luaL_error(L, "Unable to encode salt."); + } - lua_pushlstring(L, (const char *)header, 96); + 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 + */ + { + 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 -ls_verify_password(lua_State *L) { - const uint8_t *header = (uint8_t *)luaL_checkstring(L, 1); - const uint8_t *passwd = (uint8_t *)luaL_checkstring(L, 2); - size_t headerlen = lua_objlen(L, 1); - size_t passwdlen = lua_objlen(L, 2); - - int N; - uint32_t r, p; - uint8_t dk[64], salt[32], hbuf[32]; - uint8_t *key_hmac = &dk[32]; - - HMAC_SHA256_CTX hctx; - SHA256_CTX ctx; - - if (headerlen != 96) { - lua_pushliteral(L, "password hash must be 96 bytes"); - lua_error(L); - } - - N = (uint64_t) 1 << header[7]; - r = be32dec(&header[8]); - p = be32dec(&header[12]); - memcpy(salt, &header[16], 32); - - SHA256_Init(&ctx); - SHA256_Update(&ctx, header, 48); - SHA256_Final(hbuf, &ctx); - - if (memcmp(&header[48], hbuf, 16)) { - lua_pushnil(L); - lua_pushliteral(L, "header SHA does not verify; hash corrupt"); - return 2; - } +luascrypt_verify_password(lua_State *L) +{ + // crypted password -> okbool + const char *crypted = luaL_checkstring(L, 1); + const char *passwd = luaL_checkstring(L, 2); - errno = 0; - if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64) != 0) { - lua_pushstring(L, strerror(errno)); - lua_error(L); - } + /* 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); - HMAC_SHA256_Init(&hctx, key_hmac, 32); - HMAC_SHA256_Update(&hctx, header, 64); - HMAC_SHA256_Final(hbuf, &hctx); + int r = libscrypt_check(crypted_copy, (char *)passwd); - if (memcmp(hbuf, &header[64], 32)) { - lua_pushboolean(L, 0); - } else { - lua_pushboolean(L, 1); + if (r < 0) { + return luaL_error(L, "Unable to verify password. Bad crypt."); } + lua_pushboolean(L, r); return 1; } -static const struct luaL_Reg -ls_functions[] = { - { "hash_password", ls_hash_password }, - { "verify_password", ls_verify_password }, - +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) { - - luaL_register(L, "scrypt", ls_functions); + luaL_register(L, "scrypt", luascrypt_functions); return 1; } -- cgit v1.2.1