From 33fb309807262347fdd8a2e3a4818dd4f1602065 Mon Sep 17 00:00:00 2001 From: "Rob Kendrick (trite)" Date: Tue, 30 Jul 2013 16:22:01 +0100 Subject: Password hashing and verification, Makefile stolen from Luxio --- Makefile | 135 +++++++++++++++++++++++++++++++++++++-- luascrypt.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 luascrypt.c diff --git a/Makefile b/Makefile index d6a39ea..8f428a9 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,109 @@ +ifeq ($(strip $(shell which pkg-config > /dev/null || echo NONE)),NONE) + $(error This makefile requires pkg-config) +endif + +ifeq ($(strip $(shell which $(CC) > /dev/null || echo NONE)),NONE) + $(info lua-scrypt: Hmm, CC make variable contains nonsense. Overriding to gcc.) + CC := gcc +endif + +NAKED_LUA_VER := $(shell (pkg-config --exists lua && \ + pkg-config --modversion lua || \ + pkg-config --variable V lua) | cut -d. -f1-2) + +# fine appropriate Lua interpreter +LUA_REPL := $(shell (which lua5.1 > /dev/null 2>&1 && echo lua5.1) || \ + (which lua-5.1 > /dev/null 2>&1 && echo lua-5.1) || \ + (which lua51 > /dev/null 2>&1 && echo lua51) || \ + (lua -e"print(_VERSION)" | grep -q 5.1 && echo lua) || \ + (which lua-5.2 > /dev/null 2>&1 && echo lua-5.2) || \ + (which lua52 > /dev/null 2>&1 && echo lua52)) + +# find appropriate Lua libraries +LUA51_PKG := $(shell (pkg-config --exists lua5.1 && echo lua5.1) || \ + (pkg-config --exists lua-5.1 && echo lua-5.1) || \ + (pkg-config --exists lua51 && echo lua51) || \ + (echo "NONE")) + + +LUA51_PKG := $(strip $(LUA51_PKG)) + +ifeq ($(LUA51_PKG),NONE) + ifeq ($(NAKED_LUA_VER),5.1) + LUA51_PKG := lua + endif +endif + +ifneq ($(LUA51_PKG),NONE) + $(info lua-scrypt: Lua 5.1 package name on this system is $(LUA51_PKG)) + LUA51_HAS_MODINST := $(shell test x`pkg-config --variable INSTALL_LMOD $(LUA51_PKG)` != x && echo YES) + LUA51_INC := $(shell pkg-config --cflags $(LUA51_PKG)) + LUA51_LIB := $(shell pkg-config --libs $(LUA51_PKG)) + ifeq ($(LUA51_HAS_MODINST),) + LOCAL := YES + endif + ifeq ($(LOCAL),) + LUA51_LMOD_INST := $(shell pkg-config --variable=INSTALL_LMOD $(LUA51_PKG)) + LUA51_CMOD_INST := $(shell pkg-config --variable=INSTALL_CMOD $(LUA51_PKG)) + else + LUA51_LMOD_INST := /usr/local/share/lua/5.1 + LUA51_CMOD_INST := /usr/local/lib/lua/5.1 + endif + lua-5.1-try: lua-5.1 + lua-5.1-try-install: lua-5.1-install +else +lua-5.1-try: + @echo lua-scrypt: Lua 5.1 could not be found, so lua-scrypt was not built for it. +lua-5.1-try-install: + @echo lua-scrypt: Lua 5.1 could not be found, so lua-scrypt was not installed for it. +endif + +LUA52_PKG := $(shell (pkg-config --exists lua5.2 && echo lua5.2) || \ + (pkg-config --exists lua-5.2 && echo lua-5.2) || \ + (pkg-config --exists lua52 && echo lua52) || \ + (echo "NONE")) + +LUA52_PKG := $(strip $(LUA52_PKG)) + +ifeq ($(LUA52_PKG),NONE) + ifeq ($(NAKED_LUA_VER),5.2) + LUA52_PKG := lua + endif +endif + +ifneq ($(LUA52_PKG),NONE) + $(info lua-scrypt: Lua 5.2 package name on this system is $(LUA52_PKG)) + LUA52_HAS_MODINST := $(shell test x`pkg-config --variable INSTALL_LMOD $(LUA52_PKG)` != x && echo YES) + LUA52_INC := $(shell pkg-config --cflags $(LUA52_PKG)) + LUA52_LIB := $(shell pkg-config --libs $(LUA52_PKG)) + ifeq ($(LUA52_HAS_MODINST),) + LOCAL := YES + endif + ifeq ($(LOCAL),) + LUA52_LMOD_INST := $(shell pkg-config --variable=INSTALL_LMOD $(LUA52_PKG)) + LUA52_CMOD_INST := $(shell pkg-config --variable=INSTALL_CMOD $(LUA52_PKG)) + else + LUA52_LMOD_INST := /usr/local/share/lua/5.2 + LUA52_CMOD_INST := /usr/local/lib/lua/5.2 + endif + lua-5.2-try: lua-5.2 + lua-5.2-try-install: lua-5.2-install +else +lua-5.2-try: + @echo lua-scrypt: Lua 5.2 could not be found, so lua-scrypt was not built for it. +lua-5.2-try-install: + @echo lua-scrypt: Lua 5.2 could not be found, so lua-scrypt was not installed for it. +endif LIBCRYPT_C := lib/crypto/crypto_aesctr.c \ lib/crypto/crypto_scrypt-nosse.c \ lib/crypto/crypto_scrypt-ref.c \ lib/crypto/sha256.c + +ifneq ($(USE_SSE),) + LIBCRYPT_C := $(subst -nosse.c$,-sse.c,$(LIBCRYPT_C)) +endif + LIBCRYPT_O := $(subst .c$,.o,$(LIBCRYPT_C)) LIBSCRYPTENC_C := lib/scryptenc/scryptenc.c lib/scryptenc/scryptenc_cpuperf.c @@ -11,19 +112,19 @@ LIBSCRYPTENC_O := $(subst .c$,.o,$(LIBSCRYPTENC_C)) LIBUTIL_C := lib/util/memlimit.c lib/util/readpass.c lib/util/warn.c LIBUTIL_O := $(subst .c$,.o,$(LIBUTIL_C)) -all: libscrypt.so +SCRYPT_LIBS := libcrypt.a libscryptenc.a libutil.a + +all: lua-5.1-try lua-5.2-try clean: - $(RM) libscrypt.so - $(RM) libcrypt.a + $(RM) scrypt-5.1.so scrypt-5.2.so scrypt.so + $(RM) $(SCRYPT_LIBS) $(RM) $(LIBCRYPT_O) $(RM) $(LIBSCRYPTENC_O) $(RM) $(LIBUTIL_O) %.o: %.c - $(CC) $(CFLAGS) -I lib/util -I lib/crypto -c $< -o $@ - -libscrypt.so: libcrypt.a libscryptenc.a libutil.a + $(CC) $(CFLAGS) -fPIC $(LUA51_INC) -I lib/util -I lib/crypto -I lib/scryptenc -c $< -o $@ libcrypt.a: $(LIBCRYPT_O) $(AR) q libcrypt.a $(LIBCRYPT_O) @@ -33,3 +134,25 @@ libscryptenc.a: $(LIBSCRYPTENC_O) libutil.a: $(LIBUTIL_O) $(AR) q libutil.a $(LIBUTIL_O) + +lua-5.1: scrypt-5.1.so + ln -s -f scrypt-5.1.so scrypt.so + +lua-5.1-install: lua-5.1 + $(INSTALL) -d $(DESTDIR)$(LUA51_CMOD_INST) + $(INSTALL) -m 755 scrypt-5.1.so $(DESTDIR)$(LUA51_CMOD_INST)/scrypt.so + +scrypt-5.1.so: luascrypt.o $(SCRYPT_LIBS) + $(CC) $(CFLAGS) -shared -o $@ $^ $(LUA51_LIB) $(SCRYPT_LIBS) + +lua-5.2: scrypt-5.2.so + ln -s -f scrypt-5.2.so scrypt.so + +lua-5.2-install: lua-5.2 + $(INSTALL) -d $(DESTDIR)$(LUA52_CMOD_INST) + $(INSTALL) -m 755 scrypt-5.2.so $(DESTDIR)$(LUA52_CMOD_INST)/scrypt.so + +scrypt-5.2.so: luascrypt.o $(SCRYPT_LIBS) + $(CC) $(CFLAGS) -shared -o $@ $^ $(LUA51_LIB) $(SCRYPT_LIBS) + + diff --git a/luascrypt.c b/luascrypt.c new file mode 100644 index 0000000..f863381 --- /dev/null +++ b/luascrypt.c @@ -0,0 +1,208 @@ +/* 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. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Barry Steyn barry.steyn@gmail.com + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sha256.h" +#include "sysendian.h" +#include "crypto_scrypt.h" +#include "memlimit.h" +#include "scryptenc_cpuperf.h" + +/* + * Obtains salt for password hash. This function is copied from Colin Percival's scrypt reference code + */ +static int +getsalt(uint8_t salt[32]) { + 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; + } + + /* Success! */ + return (0); + +err1: + close(fd); +err0: + /* Failure! */ + return (4); +} + +static int +ls_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); + } + + errno = 0; + if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64) != 0) { + lua_pushstring(L, strerror(errno)); + lua_error(L); + } + + 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); + + SHA256_Init(&ctx); + SHA256_Update(&ctx, header, 48); + SHA256_Final(hbuf, &ctx); + memcpy(&header[48], hbuf, 16); + + HMAC_SHA256_Init(&hctx, key_hmac, 32); + HMAC_SHA256_Update(&hctx, header, 64); + HMAC_SHA256_Final(hbuf, &hctx); + memcpy(&header[64], hbuf, 32); + + lua_pushlstring(L, (const char *)header, 96); + 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; + } + + errno = 0; + if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64) != 0) { + lua_pushstring(L, strerror(errno)); + lua_error(L); + } + + HMAC_SHA256_Init(&hctx, key_hmac, 32); + HMAC_SHA256_Update(&hctx, header, 64); + HMAC_SHA256_Final(hbuf, &hctx); + + if (memcmp(hbuf, &header[64], 32)) { + lua_pushboolean(L, 0); + } else { + lua_pushboolean(L, 1); + } + + return 1; +} + +static const struct luaL_Reg +ls_functions[] = { + { "hash_password", ls_hash_password }, + { "verify_password", ls_verify_password }, + + { NULL, NULL } +}; + +int +luaopen_scrypt(lua_State *L) +{ + + luaL_register(L, "scrypt", ls_functions); + return 1; +} -- cgit v1.2.1