]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Add preliminary lua API for libcryptobox
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 19 Feb 2016 22:54:08 +0000 (22:54 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 19 Feb 2016 22:54:08 +0000 (22:54 +0000)
src/lua/CMakeLists.txt
src/lua/lua_cryptobox.c [new file with mode: 0644]

index 31b4bfae7e74cff83da55540b94db802e47c83bf..3d3a7cebfab02b5b9618fb4b5383696393ba312a 100644 (file)
@@ -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 (file)
index 0000000..0f4c4ea
--- /dev/null
@@ -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);
+}