From 10e385ec78211ed42eb19fb413a59dfa09e8d5d8 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 19 Feb 2016 22:54:08 +0000 Subject: [PATCH] Add preliminary lua API for libcryptobox --- src/lua/CMakeLists.txt | 3 +- src/lua/lua_cryptobox.c | 673 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 675 insertions(+), 1 deletion(-) create mode 100644 src/lua/lua_cryptobox.c diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt index 31b4bfae7..3d3a7cebf 100644 --- a/src/lua/CMakeLists.txt +++ b/src/lua/CMakeLists.txt @@ -25,6 +25,7 @@ SET(LUASRC ${CMAKE_CURRENT_SOURCE_DIR}/lua_common.c ${CMAKE_CURRENT_SOURCE_DIR}/lua_tcp.c ${CMAKE_CURRENT_SOURCE_DIR}/lua_html.c ${CMAKE_CURRENT_SOURCE_DIR}/lua_fann.c - ${CMAKE_CURRENT_SOURCE_DIR}/lua_sqlite3.c) + ${CMAKE_CURRENT_SOURCE_DIR}/lua_sqlite3.c + ${CMAKE_CURRENT_SOURCE_DIR}/lua_cryptobox.c) SET(RSPAMD_LUA ${LUASRC} PARENT_SCOPE) diff --git a/src/lua/lua_cryptobox.c b/src/lua/lua_cryptobox.c new file mode 100644 index 000000000..0f4c4ea5f --- /dev/null +++ b/src/lua/lua_cryptobox.c @@ -0,0 +1,673 @@ +/*- + * Copyright 2016 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*- + * Copyright 2016 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file lua_cryptobox.c + * This module exports routines to load cryptobox keys, check inline or external + * crypto signatures or encrypting/decrypting data. + */ + +#include "lua_common.h" +#include "cryptobox.h" +#include "keypair.h" +#include "unix-std.h" + +LUA_FUNCTION_DEF (cryptobox_pubkey, load); +LUA_FUNCTION_DEF (cryptobox_pubkey, create); +LUA_FUNCTION_DEF (cryptobox_pubkey, gc); +LUA_FUNCTION_DEF (cryptobox_keypair, load); +LUA_FUNCTION_DEF (cryptobox_keypair, create); +LUA_FUNCTION_DEF (cryptobox_keypair, gc); +LUA_FUNCTION_DEF (cryptobox_signature, create); +LUA_FUNCTION_DEF (cryptobox_signature, load); +LUA_FUNCTION_DEF (cryptobox_signature, save); +LUA_FUNCTION_DEF (cryptobox_signature, gc); +LUA_FUNCTION_DEF (cryptobox, verify_memory); +LUA_FUNCTION_DEF (cryptobox, verify_file); +LUA_FUNCTION_DEF (cryptobox, sign_file); +LUA_FUNCTION_DEF (cryptobox, sign_memory); + +static const struct luaL_reg cryptoboxlib_f[] = { + LUA_INTERFACE_DEF (cryptobox, verify_memory), + LUA_INTERFACE_DEF (cryptobox, verify_file), + LUA_INTERFACE_DEF (cryptobox, sign_memory), + LUA_INTERFACE_DEF (cryptobox, sign_file), + {NULL, NULL} +}; + +static const struct luaL_reg cryptoboxpubkeylib_f[] = { + LUA_INTERFACE_DEF (cryptobox_pubkey, load), + LUA_INTERFACE_DEF (cryptobox_pubkey, create), + {NULL, NULL} +}; + +static const struct luaL_reg cryptoboxpubkeylib_m[] = { + {"__tostring", rspamd_lua_class_tostring}, + {"__gc", lua_cryptobox_pubkey_gc}, + {NULL, NULL} +}; + +static const struct luaL_reg cryptoboxkeypairlib_f[] = { + LUA_INTERFACE_DEF (cryptobox_keypair, load), + LUA_INTERFACE_DEF (cryptobox_keypair, create), + {NULL, NULL} +}; + +static const struct luaL_reg cryptoboxkeypairlib_m[] = { + {"__tostring", rspamd_lua_class_tostring}, + {"__gc", lua_cryptobox_keypair_gc}, + {NULL, NULL} +}; + +static const struct luaL_reg cryptoboxsignlib_f[] = { + LUA_INTERFACE_DEF (cryptobox_signature, load), + LUA_INTERFACE_DEF (cryptobox_signature, create), + {NULL, NULL} +}; + +static const struct luaL_reg cryptoboxsignlib_m[] = { + LUA_INTERFACE_DEF (cryptobox_signature, save), + {"__tostring", rspamd_lua_class_tostring}, + {"__gc", lua_cryptobox_signature_gc}, + {NULL, NULL} +}; + +static struct rspamd_cryptobox_pubkey * +lua_check_cryptobox_pubkey (lua_State * L, int pos) +{ + void *ud = luaL_checkudata (L, pos, "rspamd{cryptobox_pubkey}"); + + luaL_argcheck (L, ud != NULL, 1, "'cryptobox_pubkey' expected"); + return ud ? *((struct rspamd_cryptobox_pubkey **)ud) : NULL; +} + +static struct rspamd_cryptobox_keypair * +lua_check_cryptobox_keypair (lua_State * L, int pos) +{ + void *ud = luaL_checkudata (L, pos, "rspamd{cryptobox_keypair}"); + + luaL_argcheck (L, ud != NULL, 1, "'cryptobox_keypair' expected"); + return ud ? *((struct rspamd_cryptobox_keypair **)ud) : NULL; +} + +static rspamd_fstring_t * +lua_check_cryptobox_sign (lua_State * L, int pos) +{ + void *ud = luaL_checkudata (L, pos, "rspamd{cryptobox_signature}"); + + luaL_argcheck (L, ud != NULL, 1, "'cryptobox_signature' expected"); + return ud ? *((rspamd_fstring_t **)ud) : NULL; +} + +/*** + * function pubkey.load(file[, type[, alg]]) + * Loads public key from base32 encoded file + * @param {string} file filename to load + * @param {string} type optional 'sign' or 'kex' for signing and encryption + * @param {string} alg optional 'default' or 'nist' for curve25519/nistp256 keys + * @return {cryptobox_pubkey} new public key + */ +static gint +lua_cryptobox_pubkey_load (lua_State *L) +{ + struct rspamd_cryptobox_pubkey *pkey = NULL, **ppkey; + const gchar *filename, *arg; + gint type = RSPAMD_KEYPAIR_SIGN; + gint alg = RSPAMD_CRYPTOBOX_MODE_25519; + guchar *map; + gsize len; + + filename = luaL_checkstring (L, 1); + if (filename != NULL) { + map = rspamd_file_xmap (filename, PROT_READ, &len); + + if (map == NULL) { + msg_err ("cannot open pubkey from file: %s, %s", + filename, + strerror (errno)); + lua_pushnil (L); + } + else { + if (lua_type (L, 2) == LUA_TSTRING) { + /* keypair type */ + arg = lua_tostring (L, 2); + + if (strcmp (arg, "sign") == 0) { + type = RSPAMD_KEYPAIR_SIGN; + } + else if (strcmp (arg, "kex") == 0) { + type = RSPAMD_KEYPAIR_KEX; + } + } + if (lua_type (L, 3) == LUA_TSTRING) { + /* algorithm */ + arg = lua_tostring (L, 3); + + if (strcmp (arg, "default") == 0 || strcmp (arg, "curve25519") == 0) { + type = RSPAMD_CRYPTOBOX_MODE_25519; + } + else if (strcmp (arg, "nist") == 0) { + type = RSPAMD_CRYPTOBOX_MODE_NIST; + } + } + + pkey = rspamd_pubkey_from_base32 (map, len, type, alg); + + if (pkey == NULL) { + msg_err ("cannot open pubkey from file: %s", filename); + munmap (map, len); + lua_pushnil (L); + } + else { + munmap (map, len); + ppkey = lua_newuserdata (L, sizeof (void *)); + rspamd_lua_setclass (L, "rspamd{cryptobox_pubkey}", -1); + *ppkey = pkey; + } + } + } + else { + lua_error (L); + } + return 1; +} + + +/*** + * function pubkey.create(data[, type[, alg]]) + * Loads public key from base32 encoded file + * @param {base32 string} base32 string with the key + * @param {string} type optional 'sign' or 'kex' for signing and encryption + * @param {string} alg optional 'default' or 'nist' for curve25519/nistp256 keys + * @return {cryptobox_pubkey} new public key + */ +static gint +lua_cryptobox_pubkey_create (lua_State *L) +{ + struct rspamd_cryptobox_pubkey *pkey = NULL, **ppkey; + const gchar *buf, *arg; + gsize len; + gint type = RSPAMD_KEYPAIR_SIGN; + gint alg = RSPAMD_CRYPTOBOX_MODE_25519; + + buf = luaL_checklstring (L, 1, &len); + if (buf != NULL) { + if (lua_type (L, 2) == LUA_TSTRING) { + /* keypair type */ + arg = lua_tostring (L, 2); + + if (strcmp (arg, "sign") == 0) { + type = RSPAMD_KEYPAIR_SIGN; + } + else if (strcmp (arg, "kex") == 0) { + type = RSPAMD_KEYPAIR_KEX; + } + } + if (lua_type (L, 3) == LUA_TSTRING) { + /* algorithm */ + arg = lua_tostring (L, 3); + + if (strcmp (arg, "default") == 0 || strcmp (arg, "curve25519") == 0) { + type = RSPAMD_CRYPTOBOX_MODE_25519; + } + else if (strcmp (arg, "nist") == 0) { + type = RSPAMD_CRYPTOBOX_MODE_NIST; + } + } + + pkey = rspamd_pubkey_from_base32 (buf, len, type, alg); + + if (pkey == NULL) { + msg_err ("cannot load pubkey from string"); + lua_pushnil (L); + } + else { + ppkey = lua_newuserdata (L, sizeof (void *)); + rspamd_lua_setclass (L, "rspamd{cryptobox_pubkey}", -1); + *ppkey = pkey; + } + + } + else { + lua_pushnil (L); + } + return 1; +} + +static gint +lua_cryptobox_pubkey_gc (lua_State *L) +{ + struct rspamd_cryptobox_pubkey *pkey = lua_check_cryptobox_pubkey (L, 1); + + if (pkey != NULL) { + rspamd_pubkey_unref (pkey); + } + + return 0; +} + +/*** + * function keypair.load(file) + * Loads public key from UCL file + * @param {string} file filename to load + * @return {cryptobox_keypair} new keypair + */ +static gint +lua_cryptobox_keypair_load (lua_State *L) +{ + struct rspamd_cryptobox_keypair *kp, **pkp; + const gchar *filename; + struct ucl_parser *parser; + ucl_object_t *obj; + + filename = luaL_checkstring (L, 1); + if (filename != NULL) { + parser = ucl_parser_new (0); + + if (!ucl_parser_add_file (parser, filename)) { + msg_err ("cannot open keypair from file: %s, %s", + filename, + ucl_parser_get_error (parser)); + ucl_parser_free (parser); + lua_pushnil (L); + } + else { + obj = ucl_parser_get_object (parser); + kp = rspamd_keypair_from_ucl (obj); + ucl_parser_free (parser); + + if (kp == NULL) { + msg_err ("cannot open keypair from file: %s", + filename); + ucl_object_unref (obj); + lua_pushnil (L); + } + else { + pkp = lua_newuserdata (L, sizeof (gpointer)); + *pkp = kp; + rspamd_lua_setclass (L, "rspamd{cryptobox_keypair}", -1); + ucl_object_unref (obj); + } + } + } + else { + lua_error (L); + } + + return 1; +} + +static gint +lua_cryptobox_keypair_create (lua_State *L) +{ + struct rspamd_cryptobox_keypair *kp, **pkp; + const gchar *buf; + gsize len; + struct ucl_parser *parser; + ucl_object_t *obj; + + buf = luaL_checklstring (L, 1, &len); + if (buf != NULL) { + parser = ucl_parser_new (0); + + if (!ucl_parser_add_chunk (parser, buf, len)) { + msg_err ("cannot open keypair from data: %s", + ucl_parser_get_error (parser)); + ucl_parser_free (parser); + lua_pushnil (L); + } + else { + obj = ucl_parser_get_object (parser); + kp = rspamd_keypair_from_ucl (obj); + ucl_parser_free (parser); + + if (kp == NULL) { + msg_err ("cannot load keypair from data"); + ucl_object_unref (obj); + lua_pushnil (L); + } + else { + pkp = lua_newuserdata (L, sizeof (gpointer)); + *pkp = kp; + rspamd_lua_setclass (L, "rspamd{cryptobox_keypair}", -1); + ucl_object_unref (obj); + } + } + } + else { + lua_error (L); + } + + return 1; +} + +static gint +lua_cryptobox_keypair_gc (lua_State *L) +{ + struct rspamd_cryptobox_keypair *kp = lua_check_cryptobox_keypair (L, 1); + + if (kp != NULL) { + rspamd_keypair_unref (kp); + } + + return 0; +} + +static gint +lua_cryptobox_signature_load (lua_State *L) +{ + rspamd_fstring_t *sig, **psig; + const gchar *filename; + gpointer data; + int fd; + struct stat st; + + filename = luaL_checkstring (L, 1); + if (filename != NULL) { + fd = open (filename, O_RDONLY); + if (fd == -1) { + msg_err ("cannot open signature file: %s, %s", filename, + strerror (errno)); + lua_pushnil (L); + } + else { + sig = g_malloc (sizeof (rspamd_fstring_t)); + if (fstat (fd, &st) == -1 || + (data = + mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) + == MAP_FAILED) { + msg_err ("cannot mmap file %s: %s", filename, strerror (errno)); + lua_pushnil (L); + } + else { + sig = rspamd_fstring_new_init (data, st.st_size); + psig = lua_newuserdata (L, sizeof (rspamd_fstring_t *)); + rspamd_lua_setclass (L, "rspamd{cryptobox_signature}", -1); + *psig = sig; + munmap (data, st.st_size); + } + close (fd); + } + } + else { + lua_pushnil (L); + } + return 1; +} + +static gint +lua_cryptobox_signature_save (lua_State *L) +{ + rspamd_fstring_t *sig; + gint fd, flags; + const gchar *filename; + gboolean forced = FALSE, res = TRUE; + + sig = lua_check_cryptobox_sign (L, 1); + filename = luaL_checkstring (L, 2); + if (lua_gettop (L) > 2) { + forced = lua_toboolean (L, 3); + } + + if (sig != NULL && filename != NULL) { + flags = O_WRONLY | O_CREAT; + if (forced) { + flags |= O_TRUNC; + } + else { + flags |= O_EXCL; + } + fd = open (filename, flags, 00644); + if (fd == -1) { + msg_err ("cannot create a signature file: %s, %s", + filename, + strerror (errno)); + lua_pushboolean (L, FALSE); + } + else { + while (write (fd, sig->str, sig->len) == -1) { + if (errno == EINTR) { + continue; + } + msg_err ("cannot write to a signature file: %s, %s", + filename, + strerror (errno)); + res = FALSE; + break; + } + lua_pushboolean (L, res); + close (fd); + } + } + else { + lua_pushboolean (L, FALSE); + } + + return 1; +} + +static gint +lua_cryptobox_signature_create (lua_State *L) +{ + rspamd_fstring_t *sig, **psig; + const gchar *data; + gsize dlen; + + data = luaL_checklstring (L, 1, &dlen); + if (data != NULL) { + sig = rspamd_fstring_new_init (data, dlen); + psig = lua_newuserdata (L, sizeof (rspamd_fstring_t *)); + rspamd_lua_setclass (L, "rspamd{cryptobox_signature}", -1); + *psig = sig; + } + + return 1; +} + +static gint +lua_cryptobox_signature_gc (lua_State *L) +{ + rspamd_fstring_t *sig = lua_check_cryptobox_sign (L, 1); + + rspamd_fstring_free (sig); + + return 0; +} + +/** + * Check memory using specified cryptobox key and signature + * + * arguments: + * (cryptobox_pubkey, cryptobox_signature, string) + * + * returns: + * true - if string match cryptobox signature + * false - otherwise + */ +static gint +lua_cryptobox_verify_memory (lua_State *L) +{ + struct rspamd_cryptobox_pubkey *pk; + rspamd_fstring_t *signature; + const gchar *data; + gsize len; + gint ret; + + pk = lua_check_cryptobox_pubkey (L, 1); + signature = lua_check_cryptobox_sign (L, 2); + /* XXX: check signature length */ + data = luaL_checklstring (L, 3, &len); + + if (pk != NULL && signature != NULL && data != NULL) { + ret = rspamd_cryptobox_verify (signature->str, data, len, + rspamd_pubkey_get_pk (pk, NULL), RSPAMD_CRYPTOBOX_MODE_25519); + + if (ret) { + lua_pushboolean (L, 1); + } + else { + lua_pushboolean (L, 0); + } + } + else { + lua_pushnil (L); + } + + return 1; +} + +/** + * Check memory using specified cryptobox key and signature + * + * arguments: + * (cryptobox_pubkey, cryptobox_signature, string) + * + * returns: + * true - if string match cryptobox signature + * false - otherwise + */ +static gint +lua_cryptobox_verify_file (lua_State *L) +{ + return 0; +} + +/** + * Sign memory using specified cryptobox key and signature + * + * arguments: + * (cryptobox_keypair, string) + * + * returns: + * rspamd_signature object + * nil - otherwise + */ +static gint +lua_cryptobox_sign_memory (lua_State *L) +{ + return 0; +} + +/** + * Sign file using specified cryptobox key and signature + * + * arguments: + * (cryptobox_keypair, cryptobox_signature, string) + * + * returns: + * true - if string match cryptobox signature + * false - otherwise + */ +static gint +lua_cryptobox_sign_file (lua_State *L) +{ + return 0; +} + +static gint +lua_load_pubkey (lua_State * L) +{ + lua_newtable (L); + luaL_register (L, NULL, cryptoboxpubkeylib_f); + + return 1; +} + +static gint +lua_load_keypair (lua_State * L) +{ + lua_newtable (L); + luaL_register (L, NULL, cryptoboxkeypairlib_f); + + return 1; +} + +static gint +lua_load_signature (lua_State * L) +{ + lua_newtable (L); + luaL_register (L, NULL, cryptoboxsignlib_f); + + return 1; +} + +static gint +lua_load_cryptobox (lua_State * L) +{ + lua_newtable (L); + luaL_register (L, NULL, cryptoboxlib_f); + + return 1; +} + +void +luaopen_cryptobox (lua_State * L) +{ + luaL_newmetatable (L, "rspamd{cryptobox_pubkey}"); + lua_pushstring (L, "__index"); + lua_pushvalue (L, -2); + lua_settable (L, -3); + + lua_pushstring (L, "class"); + lua_pushstring (L, "rspamd{cryptobox_pubkey}"); + lua_rawset (L, -3); + + luaL_register (L, NULL, cryptoboxpubkeylib_m); + rspamd_lua_add_preload (L, "rspamd_cryptobox_pubkey", lua_load_pubkey); + + luaL_newmetatable (L, "rspamd{cryptobox_keypair}"); + lua_pushstring (L, "__index"); + lua_pushvalue (L, -2); + lua_settable (L, -3); + + lua_pushstring (L, "class"); + lua_pushstring (L, "rspamd{cryptobox_keypair}"); + lua_rawset (L, -3); + + luaL_register (L, NULL, cryptoboxkeypairlib_m); + rspamd_lua_add_preload (L, "rspamd_cryptobox_keypair", lua_load_keypair); + + luaL_newmetatable (L, "rspamd{cryptobox_signature}"); + lua_pushstring (L, "__index"); + lua_pushvalue (L, -2); + lua_settable (L, -3); + + lua_pushstring (L, "class"); + lua_pushstring (L, "rspamd{cryptobox_signature}"); + lua_rawset (L, -3); + + luaL_register (L, NULL, cryptoboxsignlib_m); + rspamd_lua_add_preload (L, "rspamd_cryptobox_signature", lua_load_signature); + + rspamd_lua_add_preload (L, "rspamd_cryptobox", lua_load_cryptobox); + + lua_settop (L, 0); +} -- 2.39.5