commit a070e5a: [Feature] Finish all features of dkim_keygen in Lua

Vsevolod Stakhov vsevolod at rspamd.com
Fri Apr 7 15:56:09 UTC 2023


Author: Vsevolod Stakhov
Date: 2023-04-07 16:38:50 +0100
URL: https://github.com/rspamd/rspamd/commit/a070e5a10a3084bf75472177bedfacc612c25071

[Feature] Finish all features of dkim_keygen in Lua

---
 lualib/rspamadm/dkim_keygen.lua |  25 +++
 src/lua/lua_cryptobox.c         |  33 ++++
 src/lua/lua_text.c              |   4 +-
 src/rspamadm/dkim_keygen.c      | 385 ----------------------------------------
 4 files changed, 60 insertions(+), 387 deletions(-)

diff --git a/lualib/rspamadm/dkim_keygen.lua b/lualib/rspamadm/dkim_keygen.lua
index a957d8fde..05be73436 100644
--- a/lualib/rspamadm/dkim_keygen.lua
+++ b/lualib/rspamadm/dkim_keygen.lua
@@ -16,6 +16,7 @@ limitations under the License.
 
 local argparse = require "argparse"
 local rspamd_util = require "rspamd_util"
+local rspamd_cryptobox = require "rspamd_cryptobox"
 
 local parser = argparse()
     :name 'rspamadm dkim_keygen'
@@ -64,6 +65,8 @@ parser:option '--priv-output'
         ['der'] = 'der',
       }
       :default 'pem'
+parser:flag '-f --force'
+      :description 'Force overwrite of existing files'
 
 local function split_string(input, max_length)
   max_length = max_length or 253
@@ -114,6 +117,9 @@ local function gen_rsa_key(opts)
 
   local sk,pk = rsa.keypair(opts.bits or 1024)
   if opts.privkey then
+    if opts.force then
+      os.remove(opts.privkey)
+    end
     sk:save(opts.privkey, opts.priv_output)
   else
     sk:save("-", opts.priv_output)
@@ -122,6 +128,25 @@ local function gen_rsa_key(opts)
   print_public_key(opts, tostring(pk))
 end
 
+local function gen_eddsa_key(opts)
+  local sk,pk = rspamd_cryptobox.gen_dkim_keypair(opts.type)
+
+  if opts.privkey and opts.force then
+    os.remove(opts.privkey)
+  end
+  if not sk:save_in_file(opts.privkey, tonumber('0600', 8)) then
+    io.stderr:write('cannot save private key to ' .. (opts.privkey or 'stdout') .. '\n')
+    os.exit(1)
+  end
+
+  if not opts.privkey then
+    io.write("\n")
+    io.flush()
+  end
+
+  print_public_key(opts, tostring(pk))
+end
+
 local function handler(args)
   local opts = parser:parse(args)
 
diff --git a/src/lua/lua_cryptobox.c b/src/lua/lua_cryptobox.c
index e342610ce..2a7b0c202 100644
--- a/src/lua/lua_cryptobox.c
+++ b/src/lua/lua_cryptobox.c
@@ -2700,6 +2700,39 @@ lua_cryptobox_gen_dkim_keypair (lua_State *L)
 		rspamd_explicit_memzero (pk, sizeof (pk));
 		rspamd_explicit_memzero (sk, sizeof (sk));
 	}
+	else if (strcmp (alg_str, "ed25519-seed") == 0) {
+		rspamd_sig_pk_t pk;
+		rspamd_sig_sk_t sk;
+		gchar *b64_data;
+		gsize b64_len;
+
+		rspamd_cryptobox_keypair_sig (pk, sk, RSPAMD_CRYPTOBOX_MODE_25519);
+
+		/* Process private key */
+		b64_data = rspamd_encode_base64 (sk,
+			32,
+			-1, &b64_len);
+
+		priv_out = lua_newuserdata (L, sizeof (*priv_out));
+		rspamd_lua_setclass (L, "rspamd{text}", -1);
+		priv_out->start = b64_data;
+		priv_out->len = b64_len;
+		priv_out->flags = RSPAMD_TEXT_FLAG_OWN|RSPAMD_TEXT_FLAG_WIPE;
+
+		/* Process public key */
+		b64_data = rspamd_encode_base64 (pk,
+			rspamd_cryptobox_pk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519),
+			-1, &b64_len);
+
+		pub_out = lua_newuserdata (L, sizeof (*pub_out));
+		rspamd_lua_setclass (L, "rspamd{text}", -1);
+		pub_out->start = b64_data;
+		pub_out->len = b64_len;
+		pub_out->flags = RSPAMD_TEXT_FLAG_OWN;
+
+		rspamd_explicit_memzero (pk, sizeof (pk));
+		rspamd_explicit_memzero (sk, sizeof (sk));
+	}
 	else {
 		return luaL_error (L, "invalid algorithm %s", alg_str);
 	}
diff --git a/src/lua/lua_text.c b/src/lua/lua_text.c
index b6f0948c3..bafab3c08 100644
--- a/src/lua/lua_text.c
+++ b/src/lua/lua_text.c
@@ -1118,12 +1118,12 @@ lua_text_save_in_file (lua_State *L)
 			fname = luaL_checkstring (L, 2);
 
 			if (lua_type (L, 3) == LUA_TNUMBER) {
-				mode = lua_tonumber (L, 3);
+				mode = lua_tointeger(L, 3);
 			}
 		}
 		else if (lua_type (L, 2) == LUA_TNUMBER) {
 			/* Created fd */
-			fd = lua_tonumber (L, 2);
+			fd = lua_tointeger (L, 2);
 		}
 
 		if (fd == -1) {
diff --git a/src/rspamadm/dkim_keygen.c b/src/rspamadm/dkim_keygen.c
deleted file mode 100644
index 1bbb71775..000000000
--- a/src/rspamadm/dkim_keygen.c
+++ /dev/null
@@ -1,385 +0,0 @@
-/*-
- * 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.
- */
-#include "config.h"
-#include "rspamadm.h"
-#include "printf.h"
-#include "str_util.h"
-#include "libcryptobox/cryptobox.h"
-#include "contrib/libottery/ottery.h"
-#include "lua/lua_common.h"
-#include "unix-std.h"
-
-#include <openssl/rsa.h>
-#include <openssl/bn.h>
-#include <openssl/pem.h>
-
-static gchar *privkey_file = NULL;
-static gchar *selector = NULL;
-static gchar *domain = NULL;
-static guint bits = 1024;
-static gchar *type = "rsa";
-
-static void rspamadm_dkim_keygen (gint argc, gchar **argv,
-								  const struct rspamadm_command *cmd);
-static const char *rspamadm_dkim_keygen_help (gboolean full_help,
-											  const struct rspamadm_command *cmd);
-static void rspamadm_dkim_keygen_lua_subrs (gpointer pL);
-
-struct rspamadm_command dkim_keygen_command = {
-		.name = "dkim_keygen",
-		.flags = 0,
-		.help = rspamadm_dkim_keygen_help,
-		.run = rspamadm_dkim_keygen,
-		.lua_subrs = rspamadm_dkim_keygen_lua_subrs,
-};
-
-static GOptionEntry entries[] = {
-		{"domain",  'd', 0, G_OPTION_ARG_STRING, &domain,
-				"Use the specified domain", NULL},
-		{"selector",  's', 0, G_OPTION_ARG_STRING, &selector,
-				"Use the specified selector", NULL},
-		{"privkey",  'k', 0, G_OPTION_ARG_STRING, &privkey_file,
-				"Save private key in the specified file", NULL},
-		{"bits",  'b', 0, G_OPTION_ARG_INT, &bits,
-				"Set key length to N bits (1024 by default)", NULL},
-		{"type",  't', 0, G_OPTION_ARG_STRING, &type,
-				"Key type: rsa or ed25519 (rsa by default)", NULL},
-		{NULL,       0,   0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
-};
-
-static const char *
-rspamadm_dkim_keygen_help (gboolean full_help, const struct rspamadm_command *cmd)
-{
-	const char *help_str;
-
-	if (full_help) {
-		help_str = "Create key pairs for dkim signing\n\n"
-				"Usage: rspamadm dkim_keygen -s selector -d domain [-k privkey] [-b bits]\n"
-				"Where options are:\n\n"
-				"-d: use the specified domain\n"
-				"-s: use the specified selector\n"
-				"-k: save private key to file instead of printing it to stdout\n"
-				"-b: set number of bits instead of 1024\n"
-				"--help: shows available options and commands";
-	}
-	else {
-		help_str = "Create dkim key pairs";
-	}
-
-	return help_str;
-}
-
-static void
-rspamd_dkim_generate_rsa_keypair (const gchar *domain, const gchar *selector,
-								  const gchar *priv_fname, const gchar *pub_fname,
-								  guint keylen)
-{
-	BIGNUM *e;
-	RSA *r;
-	BIO *pubout, *privout;
-	EVP_PKEY *pk;
-	gint rc;
-	glong publen;
-	gsize b64_len;
-	gchar *pubdata, *b64_data;
-	FILE *pubfile = NULL;
-
-	if (bits > 4096 || bits < 512) {
-		fprintf (stderr, "Bits number must be in the interval 512...4096\n");
-		exit (EXIT_FAILURE);
-	}
-
-	e = BN_new ();
-	r = RSA_new ();
-	pk = EVP_PKEY_new ();
-	g_assert (BN_set_word (e, RSA_F4) == 1);
-	g_assert (RSA_generate_key_ex (r, bits, e, NULL) == 1);
-	g_assert (EVP_PKEY_set1_RSA (pk, r) == 1);
-
-	if (priv_fname) {
-		int fd = open (priv_fname, O_WRONLY | O_CREAT | O_TRUNC, 0640);
-
-		if (fd < 0) {
-			rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-					priv_fname, strerror (errno));
-			exit (EXIT_FAILURE);
-		}
-
-		FILE *fp = fdopen (fd, "w");
-
-		if (fp == NULL) {
-			close (fd);
-			rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-					priv_fname, strerror (errno));
-			exit (EXIT_FAILURE);
-		}
-
-		privout = BIO_new_fp (fp, BIO_CLOSE);
-
-		if (privout == NULL) {
-			fclose (fp);
-			rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-					priv_fname, strerror (errno));
-			exit (EXIT_FAILURE);
-		}
-	} else {
-		privout = BIO_new_fp (stdout, BIO_NOCLOSE);
-	}
-
-	rc = PEM_write_bio_PrivateKey (privout, pk, NULL, NULL, 0, NULL, NULL);
-
-	if (rc != 1) {
-		rspamd_fprintf (stderr, "cannot write key to the output file %s: %s\n",
-				priv_fname ? priv_fname : "stdout", strerror (errno));
-		exit (EXIT_FAILURE);
-	}
-
-	BIO_free (privout);
-	fflush (stdout);
-
-	pubout = BIO_new (BIO_s_mem ());
-
-	rc = i2d_RSA_PUBKEY_bio (pubout, r);
-	publen = BIO_get_mem_data (pubout, &pubdata);
-
-	g_assert (publen > 0);
-	b64_data = rspamd_encode_base64 (pubdata, publen, -1, &b64_len);
-
-	if (pub_fname) {
-		pubfile = fopen (pub_fname, "w");
-
-		if (pubfile == NULL) {
-			rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-					pub_fname, strerror (errno));
-			exit (EXIT_FAILURE);
-		}
-	} else {
-		pubfile = stdout;
-	}
-
-	if (b64_len < 255 - 2) {
-		rspamd_fprintf (pubfile, "%s._domainkey IN TXT ( \"v=DKIM1; k=rsa; \"\n"
-								 "\t\"p=%s\" ) ;\n",
-				selector ? selector : "selector",
-				b64_data);
-	} else {
-		guint i;
-		gint step = 253, remain = b64_len;
-
-		rspamd_fprintf (pubfile, "%s._domainkey IN TXT ( \"v=DKIM1; k=rsa; \"\n",
-				selector ? selector : "selector");
-
-		for (i = 0; i < b64_len; i += step, remain -= step) {
-			if (i == 0) {
-				rspamd_fprintf (pubfile, "\t\"p=%*s\"\n", MIN(step, remain), &b64_data[i]);
-			} else {
-				step = 255;
-				rspamd_fprintf (pubfile, "\t\"%*s\"\n", MIN(step, remain), &b64_data[i]);
-			}
-		}
-
-		rspamd_fprintf (pubfile, ") ; \n");
-	}
-
-	if (pubfile != stdout) {
-		fclose (pubfile);
-	}
-
-	g_free (b64_data);
-	BIO_free (pubout);
-	EVP_PKEY_free (pk);
-	RSA_free (r);
-	BN_free (e);
-}
-
-static void
-rspamd_dkim_generate_ed25519_keypair (const gchar *domain, const gchar *selector,
-								  const gchar *priv_fname, const gchar *pub_fname,
-								  guint keylen, gboolean seeded)
-{
-	rspamd_sig_sk_t ed_sk;
-	rspamd_sig_pk_t ed_pk;
-	gchar *base64_pk, *base64_sk;
-	FILE *pubfile = NULL, *privfile = NULL;
-
-	rspamd_cryptobox_keypair_sig (ed_pk, ed_sk, RSPAMD_CRYPTOBOX_MODE_25519);
-	if (seeded) {
-		/* Just encode seed, not the full sk */
-		base64_sk = rspamd_encode_base64_common (ed_sk, 32, 0, NULL, FALSE,
-				RSPAMD_TASK_NEWLINES_LF);
-	}
-	else {
-		base64_sk = rspamd_encode_base64_common (ed_sk,
-				rspamd_cryptobox_sk_sig_bytes (RSPAMD_CRYPTOBOX_MODE_25519),
-				0, NULL, FALSE,
-				RSPAMD_TASK_NEWLINES_LF);
-	}
-	base64_pk = rspamd_encode_base64_common (ed_pk, sizeof (ed_pk), 0, NULL, FALSE,
-			RSPAMD_TASK_NEWLINES_LF);
-
-	/* Cleanup sensitive data */
-	rspamd_explicit_memzero (ed_sk, sizeof (ed_sk));
-
-	if (priv_fname) {
-		privfile = fopen (priv_fname, "w");
-
-		if (privfile == NULL) {
-			rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-					priv_fname, strerror (errno));
-			rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-			g_free (base64_sk);
-			g_free (base64_pk);
-			exit (EXIT_FAILURE);
-		}
-	}
-	else {
-		privfile = stdout;
-	}
-
-	if (rspamd_fprintf (privfile, "%s\n", base64_sk) == -1) {
-		rspamd_fprintf (stderr, "cannot write to output file %s: %s\n",
-				priv_fname, strerror (errno));
-		rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-		g_free (base64_sk);
-		g_free (base64_pk);
-
-		if (privfile != stdout) {
-			fclose (privfile);
-		}
-
-		exit (EXIT_FAILURE);
-	}
-
-	if (privfile != stdout) {
-		fclose (privfile);
-	}
-
-	if (pub_fname) {
-		pubfile = fopen (pub_fname, "w");
-
-		if (pubfile == NULL) {
-			rspamd_fprintf (stderr, "cannot open output file %s: %s\n",
-					pub_fname, strerror (errno));
-			rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-			g_free (base64_sk);
-			g_free (base64_pk);
-			exit (EXIT_FAILURE);
-		}
-	}
-	else {
-		pubfile = stdout;
-	}
-
-	rspamd_fprintf (pubfile, "%s._domainkey IN TXT ( \"v=DKIM1; k=ed25519; \"\n"
-							 "\t\"p=%s\" ) ;\n",
-			selector ? selector : "selector",
-			base64_pk);
-
-	if (pubfile != stdout) {
-		fclose (pubfile);
-	}
-
-	rspamd_explicit_memzero (base64_sk, strlen (base64_sk));
-	g_free (base64_sk);
-	g_free (base64_pk);
-}
-
-static void
-rspamadm_dkim_generate_keypair (const gchar *domain, const gchar *selector,
-		const gchar *priv_fname, const gchar *pub_fname, guint keylen)
-{
-	if (strcmp (type, "rsa") == 0) {
-		rspamd_dkim_generate_rsa_keypair (domain, selector, priv_fname,
-				pub_fname, keylen);
-	}
-	else if (strcmp (type, "ed25519") == 0) {
-		rspamd_dkim_generate_ed25519_keypair (domain, selector, priv_fname,
-				pub_fname, keylen, FALSE);
-	}
-	else if (strcmp (type, "ed25519-seed") == 0) {
-		rspamd_dkim_generate_ed25519_keypair (domain, selector, priv_fname,
-				pub_fname, keylen, TRUE);
-	}
-	else {
-		fprintf (stderr, "invalid key type: %s\n", type);
-		exit (EXIT_FAILURE);
-	}
-}
-
-static gint
-rspamadm_dkim_keygen_lua_generate (lua_State *L)
-{
-	const gchar *domain = luaL_checkstring (L, 1);
-	const gchar *selector = luaL_checkstring (L, 2);
-	const gchar *privfile = NULL, *pubfile = NULL;
-	guint key_bits = 1024;
-
-	if (domain == NULL || selector == NULL) {
-		return luaL_error (L, "invalid arguments");
-	}
-
-	if (lua_type (L, 3) == LUA_TSTRING) {
-		privfile = lua_tostring (L, 3);
-	}
-
-	if (lua_type (L, 4) == LUA_TSTRING) {
-		pubfile = lua_tostring (L, 4);
-	}
-
-	if (lua_type (L, 5) == LUA_TNUMBER) {
-		key_bits = lua_tonumber (L, 5);
-	}
-
-	rspamadm_dkim_generate_keypair (domain, selector, privfile, pubfile, key_bits);
-
-	return 0;
-}
-
-static void
-rspamadm_dkim_keygen_lua_subrs (gpointer pL)
-{
-	lua_State *L = pL;
-
-	lua_pushstring (L, "dkim_keygen");
-	lua_pushcfunction (L, rspamadm_dkim_keygen_lua_generate);
-	lua_settable (L, -3);
-}
-
-static void
-rspamadm_dkim_keygen (gint argc, gchar **argv, const struct rspamadm_command *cmd)
-{
-	GOptionContext *context;
-	GError *error = NULL;
-
-	context = g_option_context_new (
-			"dkim_keygen - create dkim keys");
-	g_option_context_set_summary (context,
-			"Summary:\n  Rspamd administration utility version "
-					RVERSION
-					"\n  Release id: "
-					RID);
-	g_option_context_add_main_entries (context, entries, NULL);
-
-	if (!g_option_context_parse (context, &argc, &argv, &error)) {
-		fprintf (stderr, "option parsing failed: %s\n", error->message);
-		g_error_free (error);
-		g_option_context_free (context);
-		exit (EXIT_FAILURE);
-	}
-
-	g_option_context_free (context);
-	rspamadm_dkim_generate_keypair (domain, selector, privkey_file, NULL, bits);
-}


More information about the Commits mailing list