commit 58bd6be: [Rework] Write dkim keygen tool in lua

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


Author: Vsevolod Stakhov
Date: 2023-04-07 16:08:15 +0100
URL: https://github.com/rspamd/rspamd/commit/58bd6be3f7f9302e51ae4031658311f9cc9842d8

[Rework] Write dkim keygen tool in lua

---
 lualib/rspamadm/dkim_keygen.lua | 144 ++++++++++++++++++++++++++++++++++++++++
 src/lua/lua_rsa.c               |   8 ++-
 src/rspamadm/CMakeLists.txt     |   1 -
 src/rspamadm/commands.c         |   2 -
 4 files changed, 151 insertions(+), 4 deletions(-)

diff --git a/lualib/rspamadm/dkim_keygen.lua b/lualib/rspamadm/dkim_keygen.lua
new file mode 100644
index 000000000..a957d8fde
--- /dev/null
+++ b/lualib/rspamadm/dkim_keygen.lua
@@ -0,0 +1,144 @@
+--[[
+Copyright (c) 2023, Vsevolod Stakhov <vsevolod at rspamd.com>
+
+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.
+]]--
+
+local argparse = require "argparse"
+local rspamd_util = require "rspamd_util"
+
+local parser = argparse()
+    :name 'rspamadm dkim_keygen'
+    :description 'Create key pairs for dkim signing'
+    :help_description_margin(30)
+parser:option '-d --domain'
+      :description 'Create a key for a specific domain'
+      :default "example.com"
+parser:option '-s --selector'
+      :description 'Create a key for a specific DKIM selector'
+      :default "mail"
+parser:option '-k --privkey'
+      :description 'Save private key to file instead of printing it to stdout'
+parser:option '-b --bits'
+      :convert(function(input)
+        local n = tonumber(input)
+        if not n or n < 512 or n > 4096 then
+          return nil
+        end
+        return n
+      end)
+      :description 'Generate an RSA key with the specified number of bits (512 to 4096)'
+      :default(1024)
+parser:option '-t --type'
+      :description 'Key type: RSA, ED25519 or ED25119-seed'
+      :convert {
+        ['rsa'] = 'rsa',
+        ['RSA'] = 'rsa',
+        ['ed25519'] = 'ed25519',
+        ['ED25519'] = 'ed25519',
+        ['ed25519-seed'] = 'ed25519-seed',
+        ['ED25519-seed'] = 'ed25519-seed',
+      }
+      :default 'rsa'
+parser:option '-o --output'
+      :description 'Output public key in the following format: dns or plain'
+      :convert {
+        ['dns'] = 'dns',
+        ['plain'] = 'plain',
+      }
+      :default 'dns'
+parser:option '--priv-output'
+      :description 'Output private key in the following format: PEM or DER (for RSA)'
+      :convert {
+        ['pem'] = 'pem',
+        ['der'] = 'der',
+      }
+      :default 'pem'
+
+local function split_string(input, max_length)
+  max_length = max_length or 253
+  local pieces = {}
+  local index = 1
+
+  while index <= #input do
+    local piece = input:sub(index, index + max_length - 1)
+    table.insert(pieces, piece)
+    index = index + max_length
+  end
+
+  return pieces
+end
+
+
+local function print_public_key_dns(opts, base64_pk)
+  if #base64_pk < 255 - 2 then
+    io.write(string.format('%s._domainkey IN TXT ( "v=DKIM1; k=rsa;" \n\t"p=%s" ) ;\n', opts.selector, base64_pk))
+  else
+    -- Split it  by parts
+    local parts = split_string(base64_pk)
+    io.write(string.format('%s._domainkey IN TXT ( "v=DKIM1; k=rsa; "\n', opts.selector))
+    for i,part in ipairs(parts) do
+      if i == 1 then
+        io.write(string.format('\t"p=%s"\n', part))
+      else
+        io.write(string.format('\t"%s"\n', part))
+      end
+    end
+    io.write(") ; \n")
+  end
+
+end
+
+local function print_public_key(opts, pk)
+  local base64_pk = tostring(rspamd_util.encode_base64(pk))
+  if opts.output == 'plain' then
+    io.write(base64_pk)
+    io.write("\n")
+  elseif opts.output == 'dns' then
+    print_public_key_dns(opts, base64_pk)
+  end
+end
+
+local function gen_rsa_key(opts)
+  local rsa = require "rspamd_rsa"
+
+  local sk,pk = rsa.keypair(opts.bits or 1024)
+  if opts.privkey then
+    sk:save(opts.privkey, opts.priv_output)
+  else
+    sk:save("-", opts.priv_output)
+  end
+
+  print_public_key(opts, tostring(pk))
+end
+
+local function handler(args)
+  local opts = parser:parse(args)
+
+  if not opts then os.exit(1) end
+
+  if opts.type == 'rsa' then
+    gen_rsa_key(opts)
+  else
+    gen_eddsa_key(opts)
+  end
+end
+
+return {
+  name = 'dkim_keygen',
+  aliases = {'dkimkeygen'},
+  handler = handler,
+  description = parser._description
+}
+
+
diff --git a/src/lua/lua_rsa.c b/src/lua/lua_rsa.c
index 571f62646..359660bc7 100644
--- a/src/lua/lua_rsa.c
+++ b/src/lua/lua_rsa.c
@@ -210,7 +210,13 @@ lua_rsa_privkey_save (lua_State *L)
 			else {
 				lua_pushboolean (L, TRUE);
 			}
-			fclose (f);
+
+			if (f != stdout) {
+				fclose (f);
+			}
+			else {
+				fflush (f);
+			}
 		}
 	}
 	else {
diff --git a/src/rspamadm/CMakeLists.txt b/src/rspamadm/CMakeLists.txt
index 13e2fb6da..125e04804 100644
--- a/src/rspamadm/CMakeLists.txt
+++ b/src/rspamadm/CMakeLists.txt
@@ -9,7 +9,6 @@ SET(RSPAMADMSRC rspamadm.c
         stat_convert.c
         signtool.c
         lua_repl.c
-        dkim_keygen.c
         ${CMAKE_BINARY_DIR}/src/workers.c
         #${CMAKE_BINARY_DIR}/src/modules.c - defined in rspamdserver
         ${CMAKE_SOURCE_DIR}/src/controller.c
diff --git a/src/rspamadm/commands.c b/src/rspamadm/commands.c
index aabf6747e..c52eecd3d 100644
--- a/src/rspamadm/commands.c
+++ b/src/rspamadm/commands.c
@@ -28,7 +28,6 @@ extern struct rspamadm_command statconvert_command;
 extern struct rspamadm_command fuzzyconvert_command;
 extern struct rspamadm_command signtool_command;
 extern struct rspamadm_command lua_command;
-extern struct rspamadm_command dkim_keygen_command;
 
 const struct rspamadm_command *commands[] = {
 	&help_command,
@@ -41,7 +40,6 @@ const struct rspamadm_command *commands[] = {
 	&fuzzyconvert_command,
 	&signtool_command,
 	&lua_command,
-	&dkim_keygen_command,
 	NULL
 };
 


More information about the Commits mailing list