/* 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; }