From 92b679d17ca41f85009c9e33cdd5967f955b5557 Mon Sep 17 00:00:00 2001 From: Ivan Stakhov <50211739+left-try@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:10:59 +0300 Subject: [Feature] Add rspamadm secretbox command * [Minor] Small fix for error messages * [Feature] Create rspamadm util to decrypt header * [Feature] Create python example to encrypt/decrypt header * [Minor] Small clean up * [Minor] Change c-rspamadm util to lua-rspamadm util * [Minor] Small clean up * [Minor] Add some debug * [Feature] Add secretbox command * [Minor] Debug * [Minor] Add additional return for encrypted string(noce + encrypted string * [Minor] Small debug * [Minor] Add a way to provide encrypted text concatenated with nonce * [Minor] Add nonce to encrypt text * [Minor] Clean up * [Minor] Clean up unused variable * [Minor] Small fix * [Minor] Fix return issue * [Minor] Add blake2b for key derivation * [Minor] Small upgrade to debug * [Minor] Small clean up * [Minor] Change return to more convenient form * [Minor] Change print to test form * [Test] Provide tests for encrypt/decrypt with rspamadm util and python script * [Minor] Change python to python3 * [Minor] Add stderr check * [Minor] Make the function return nonce+text * [Minor] Change unit tests to new return format * [Minor] Add flag to manage encodings * [Minor] Add --encoding argument to manage encodings * [Minor] Change tests for new input format * [Minor] Fix lua format * [Minor] Small fix * [Minor] Provide full support for new return format of maybe_encrypt_header * [Test] Test small fix * [Test] Small fix * [Minor] Clean up * [Minor] Small fix for name of variable * [Minor] Small clean up * [Minor] Change format of command to a mre convenient * [Minor] Change tests to be same as a format of a command * [Minor] Change description of flags * [Minor] Small fix --------- Co-authored-by: Ivan Stakhov <50211739+LeftTry@users.noreply.github.com> --- lualib/lua_util.lua | 11 +-- lualib/rspamadm/secretbox.lua | 157 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 lualib/rspamadm/secretbox.lua (limited to 'lualib') diff --git a/lualib/lua_util.lua b/lualib/lua_util.lua index ffc07842e..62b38c87e 100644 --- a/lualib/lua_util.lua +++ b/lualib/lua_util.lua @@ -1305,12 +1305,12 @@ exports.maybe_encrypt_header = function(header, settings, prefix) local rspamd_secretbox = require "rspamd_cryptobox_secretbox" if not header or header == '' then - logger.errx(rspamd_config, "Header: %s is empty or nil", header) + logger.errx(rspamd_config, "Header is empty or nil. Header: %s", header) return nil elseif settings[prefix .. '_encrypt'] then local key = settings[prefix .. '_key'] if not key or key == '' then - logger.errx(rspamd_config, "Key: %s is empty or nil", key) + logger.errx(rspamd_config, "Key is empty or nil. Key: %s", key) return header end local cryptobox = rspamd_secretbox.create(key) @@ -1322,7 +1322,8 @@ exports.maybe_encrypt_header = function(header, settings, prefix) else encrypted_header = cryptobox:encrypt(header, nonce) end - return encrypted_header, nonce + encrypted_header = nonce .. encrypted_header + return encrypted_header end end @@ -1342,12 +1343,12 @@ exports.maybe_decrypt_header = function(encrypted_header, settings, prefix, nonc local rspamd_secretbox = require "rspamd_cryptobox_secretbox" if not encrypted_header or encrypted_header == '' then - logger.errx(rspamd_config, "Encoded header: %s is empty or nil") + logger.errx(rspamd_config, "Encrypted header is empty or nil. Encrypted header: %s", encrypted_header) return nil elseif settings[prefix .. '_encrypt'] then local key = settings[prefix .. '_key'] if not key or key == '' then - logger.errx(rspamd_config, "Key: %s is empty or nil") + logger.errx(rspamd_config, "Key is empty or nil. Key: %s", key) return encrypted_header end local cryptobox = rspamd_secretbox.create(key) diff --git a/lualib/rspamadm/secretbox.lua b/lualib/rspamadm/secretbox.lua new file mode 100644 index 000000000..3ab10cee0 --- /dev/null +++ b/lualib/rspamadm/secretbox.lua @@ -0,0 +1,157 @@ +local util = require 'lua_util' +local rspamd_util = require 'rspamd_util' +local argparse = require 'argparse' + +local parser = argparse() + :name "secretbox" + :description "Encrypt/decrypt given text with given key and nonce" + :help_description_margin(32) + :command_target('command') + :require_command(true) + +parser:mutex( + parser:flag '-R --raw' + :description('Encrypted text(and nonce if it is there) will be given in raw'), + parser:flag '-H --hex' + :description('Encrypted text(and nonce if it is there) will be given in hex'), + parser:flag '-b --base32' + :description('Encrypted text(and nonce if it is there) will be given in base32'), + parser:flag '-B --base64' + :description('Encrypted text(and nonce if it is there) will be given in base64') +) + +local decrypt = parser:command 'decrypt' + :description 'Decrypt text with given key and nonce' + +decrypt:option "-t --text" + :description("Encrypted text(Base 64)") + :argname("") +decrypt:option "-k --key" + :description("Key used to encrypt text") + :argname("") +decrypt:option "-n --nonce" + :description("Nonce used to encrypt text(Base 64)") + :argname("") + :default(nil) + +local encrypt = parser:command 'encrypt' + :description 'Encrypt text with given key' + +encrypt:option "-t --text" + :description("Text to encrypt") + :argname("") +encrypt:option "-k --key" + :description("Key to encrypt text") + :argname("") +encrypt:option "-n --nonce" + :description("Nonce to encrypt text(Base 64)") + :argname("") + :default(nil) + +local function set_up_encoding(args, type, text) + local function fromhex(str) + return (str:gsub('..', function (cc) + return string.char(tonumber(cc, 16)) + end)) + end + + local function tohex(str) + return (str:gsub('.', function (c) + return string.format('%02X', string.byte(c)) + end)) + end + + local output = text + + if type == 'encode' then + if args.hex then + output = tohex(text) + elseif args.base32 then + output = rspamd_util.encode_base32(text) + elseif args.base64 then + output = rspamd_util.encode_base64(text) + end + elseif type == 'decode' then + if args.hex then + output = fromhex(text) + elseif args.base32 then + output = rspamd_util.decode_base32(text) + elseif args.base64 then + output = rspamd_util.decode_base64(text) + end + end + + return output +end + +local function decryption_handler(args) + local settings = { + prefix = 'dec', + dec_encrypt = true, + dec_key = args.key + } + + local decrypted_header = '' + if(args.nonce ~= nil) then + local decoded_text = set_up_encoding(args, 'decode', tostring(args.text)) + local decoded_nonce = set_up_encoding(args, 'decode', tostring(args.nonce)) + + decrypted_header = util.maybe_decrypt_header(decoded_text, settings, settings.prefix, decoded_nonce) + else + local text_with_nonce = set_up_encoding(args, 'decode', tostring(args.text)) + local nonce = string.sub(tostring(text_with_nonce), 1, 24) + local text = string.sub(tostring(text_with_nonce), 25) + + decrypted_header = util.maybe_decrypt_header(text, settings, settings.prefix, nonce) + end + + if decrypted_header ~= nil then + print(decrypted_header) + else + print('The decryption failed. Please check the correctness of the arguments given.') + end +end + +local function encryption_handler(args) + local settings = { + prefix = 'dec', + dec_encrypt = true, + dec_key = args.key, + } + + if args.nonce ~= nil then + settings.dec_nonce = set_up_encoding(args, 'decode', tostring(args.nonce)) + end + + local encrypted_text = util.maybe_encrypt_header(args.text, settings, settings.prefix) + + if encrypted_text ~= nil then + print(set_up_encoding(args, 'encode', tostring(encrypted_text))) + else + print('The encryption failed. Please check the correctness of the arguments given.') + end +end + +local command_handlers = { + decrypt = decryption_handler, + encrypt = encryption_handler, +} + +local function handler(args) + local cmd_opts = parser:parse(args) + + local f = command_handlers[cmd_opts.command] + if not f then + parser:error(string.format("command isn't implemented: %s", + cmd_opts.command)) + end + f(cmd_opts) +end + + +return { + name = 'secret_box', + aliases = { 'secretbox', 'secret_box' }, + handler = handler, + description = parser._description +} \ No newline at end of file -- cgit v1.2.3