summaryrefslogtreecommitdiff
path: root/luascrypt.c
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2015-07-08 19:54:40 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2015-07-08 19:54:40 +0100
commit9affdc735bc5731580619057405d16cf588afe9b (patch)
treee813c32a03abc78201f005361238d976b0ca0773 /luascrypt.c
parentb2378b42f08b6582ae891e8d8599dde474e032ba (diff)
downloadlua-scrypt-git-9affdc735bc5731580619057405d16cf588afe9b.tar.gz
Change to using libscrypt even though it has known failure modes
Diffstat (limited to 'luascrypt.c')
-rw-r--r--luascrypt.c271
1 files changed, 106 insertions, 165 deletions
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 <rjek+luascrypt@rjek.com>
- *
- * 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 <dsilvers@digital-scurf.org>
*
- * Barry Steyn barry.steyn@gmail.com
+ * Please see the file COPYING for licence details.
*/
-#include <stdlib.h>
-#include <string.h>
+#include <stdint.h>
#include <errno.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
-#include <stdint.h>
-#include <math.h>
-#include <lua.h>
-#include <lauxlib.h>
+#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;
}