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 --- luascrypt.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 luascrypt.c (limited to 'luascrypt.c') 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