diff options
author | Vsevolod Stakhov <vsevolod@rspamd.com> | 2024-09-24 22:09:51 +0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 22:09:51 +0600 |
commit | f8700e56ec5659f331f0aca0b28eec43a4e7cb33 (patch) | |
tree | 999c6c7bd2ea52fc5352ed03275f4cef0cfa80c7 | |
parent | 3dda59641af8826d50dd07bc82d67c9ffecef403 (diff) | |
parent | 464045f814b78b2d4f9ca20cc9e0a0a1b323ad8e (diff) | |
download | rspamd-f8700e56ec5659f331f0aca0b28eec43a4e7cb33.tar.gz rspamd-f8700e56ec5659f331f0aca0b28eec43a4e7cb33.zip |
Merge branch 'master' into vstakhov-utf8-mimevstakhov-utf8-mime
-rw-r--r-- | .github/workflows/ci.yml | 18 | ||||
-rw-r--r-- | .github/workflows/ci_rspamd_build.yml | 53 | ||||
-rw-r--r-- | CMakeLists.txt | 11 | ||||
-rw-r--r-- | config.h.in | 1 | ||||
-rw-r--r-- | lualib/lua_util.lua | 11 | ||||
-rw-r--r-- | lualib/rspamadm/secretbox.lua | 157 | ||||
-rw-r--r-- | src/fuzzy_storage.c | 9 | ||||
-rw-r--r-- | src/libcryptobox/cryptobox.c | 55 | ||||
-rw-r--r-- | src/libcryptobox/keypair.c | 151 | ||||
-rw-r--r-- | src/libcryptobox/keypair.h | 51 | ||||
-rw-r--r-- | src/libserver/http/http_connection.c | 6 | ||||
-rw-r--r-- | src/libserver/maps/map.c | 6 | ||||
-rw-r--r-- | src/lua/lua_cryptobox.c | 49 | ||||
-rw-r--r-- | src/lua/lua_map.c | 4 | ||||
-rw-r--r-- | src/lua/lua_rsa.c | 20 | ||||
-rw-r--r-- | src/plugins/lua/dmarc.lua | 12 | ||||
-rw-r--r-- | src/rspamadm/signtool.c | 3 | ||||
-rw-r--r-- | test/functional/cases/150_rspamadm.robot | 47 | ||||
-rw-r--r-- | test/lua/unit/lua_util.maybe_encrypt_decrypt_header.lua | 11 | ||||
-rw-r--r-- | utils/encrypt_decrypt_header.py | 97 |
20 files changed, 649 insertions, 123 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d92d5105e..07c4cc4d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,3 +23,21 @@ jobs: with: image: ghcr.io/rspamd/rspamd-build-docker:ubuntu-ci name: ubuntu-ci + + ubuntu-focal: + uses: ./.github/workflows/ci_rspamd_build.yml + with: + image: ghcr.io/rspamd/rspamd-build-docker:ubuntu-focal + name: ubuntu-focal + + centos-8: + uses: ./.github/workflows/ci_rspamd_build.yml + with: + image: ghcr.io/rspamd/rspamd-build-docker:centos-8 + name: centos-8 + + centos-9: + uses: ./.github/workflows/ci_rspamd_build.yml + with: + image: ghcr.io/rspamd/rspamd-build-docker:centos-9 + name: centos-9 diff --git a/.github/workflows/ci_rspamd_build.yml b/.github/workflows/ci_rspamd_build.yml new file mode 100644 index 000000000..9503f1974 --- /dev/null +++ b/.github/workflows/ci_rspamd_build.yml @@ -0,0 +1,53 @@ +name: rspamd_build + +on: + workflow_call: + inputs: + image: + required: true + type: string + name: + required: true + type: string + +jobs: + test: + runs-on: [ "ubuntu-latest" ] + container: + image: ${{ inputs.image }} + options: --user root + steps: + - name: Create directories + run: | + sudo mkdir -p ${GITHUB_WORKSPACE} + + - name: Check out source code + uses: actions/checkout@v4 + with: + path: src + + - name: Set variables on ARM64 + if: runner.arch == 'ARM64' + run: echo "HYPERSCAN_ALTROOT=-DHYPERSCAN_ROOT_DIR=/vectorscan" >> "$GITHUB_ENV" + + - name: Run cmake + run: | + mkdir ${GITHUB_WORKSPACE}/build + cd ${GITHUB_WORKSPACE}/build + if [[ -f /opt/rh/gcc-toolset-10/enable ]] ; then source /opt/rh/gcc-toolset-10/enable ; fi + cmake -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_COVERAGE=ON -DENABLE_LIBUNWIND=ON -DENABLE_LUAJIT=ON -DLUA_ROOT=/luajit-build -DENABLE_HYPERSCAN=ON ${{ env.HYPERSCAN_ALTROOT }} ${GITHUB_WORKSPACE}/src + + - name: Build rspamd + run: | + if [[ -f /opt/rh/gcc-toolset-10/enable ]] ; then source /opt/rh/gcc-toolset-10/enable ; fi + cd ${GITHUB_WORKSPACE}/build + ncpu=$(getconf _NPROCESSORS_ONLN) + make -j $ncpu install + make -j $ncpu rspamd-test + make -j $ncpu rspamd-test-cxx + + - name: Run unit tests + if: "!(inputs.name == 'ubuntu-ci' && runner.arch == 'ARM64')" + run: | + cd ${GITHUB_WORKSPACE}/build + make test diff --git a/CMakeLists.txt b/CMakeLists.txt index a9bb50642..8028e170e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,8 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.12 FATAL_ERROR) SET(RSPAMD_VERSION_MAJOR 3) -SET(RSPAMD_VERSION_MINOR 9) -SET(RSPAMD_VERSION_PATCH 2) +SET(RSPAMD_VERSION_MINOR 10) +SET(RSPAMD_VERSION_PATCH 0) # Keep two digits all the time SET(RSPAMD_VERSION_MAJOR_NUM ${RSPAMD_VERSION_MAJOR}0) @@ -472,6 +472,13 @@ int main(int argc, char **argv) { } " HAVE_ATOMIC_BUILTINS) +CHECK_C_SOURCE_RUNS("#include <stdio.h> +int main() { + __builtin_cpu_init(); + printf(\"%d\", __builtin_cpu_supports(\"avx\")); + return 0; +}" HAVE_BUILTIN_CPU_SUPPORTS) + IF (NOT HAVE_ATOMIC_BUILTINS) MESSAGE(STATUS "atomic builtins are -NOT- supported") ELSE () diff --git a/config.h.in b/config.h.in index 59c70c67c..9aff90783 100644 --- a/config.h.in +++ b/config.h.in @@ -115,6 +115,7 @@ #cmakedefine WITH_LUA_REPL 1 #cmakedefine WITH_FASTTEXT 1 #cmakedefine BACKWARD_ENABLE 1 +#cmakedefine HAVE_BUILTIN_CPU_SUPPORTS 1 #cmakedefine DISABLE_PTHREAD_MUTEX 1 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("<text>") +decrypt:option "-k --key" + :description("Key used to encrypt text") + :argname("<key>") +decrypt:option "-n --nonce" + :description("Nonce used to encrypt text(Base 64)") + :argname("<nonce>") + :default(nil) + +local encrypt = parser:command 'encrypt' + :description 'Encrypt text with given key' + +encrypt:option "-t --text" + :description("Text to encrypt") + :argname("<text>") +encrypt:option "-k --key" + :description("Key to encrypt text") + :argname("<key>") +encrypt:option "-n --nonce" + :description("Nonce to encrypt text(Base 64)") + :argname("<nonce>") + :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 diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c index 841d040b2..f21992a94 100644 --- a/src/fuzzy_storage.c +++ b/src/fuzzy_storage.c @@ -2453,7 +2453,8 @@ rspamd_fuzzy_key_stat_iter(const unsigned char *pk_iter, struct fuzzy_key *fuzzy ucl_object_insert_key(elt, flags_ucl, "flags", 0, false); ucl_object_insert_key(elt, - rspamd_keypair_to_ucl(fuzzy_key->key, RSPAMD_KEYPAIR_DUMP_NO_SECRET | RSPAMD_KEYPAIR_DUMP_FLATTENED), + rspamd_keypair_to_ucl(fuzzy_key->key, RSPAMD_KEYPAIR_ENCODING_DEFAULT, + RSPAMD_KEYPAIR_DUMP_NO_SECRET | RSPAMD_KEYPAIR_DUMP_FLATTENED), "keypair", 0, false); ucl_object_insert_key(keys_obj, elt, keyname, 0, true); } @@ -2474,6 +2475,12 @@ rspamd_fuzzy_stat_to_ucl(struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat) rspamd_fuzzy_key_stat_iter(pk_iter, fuzzy_key, keys_obj, ip_stat); }); + if (ctx->dynamic_keys) { + kh_foreach(ctx->dynamic_keys, pk_iter, fuzzy_key, { + rspamd_fuzzy_key_stat_iter(pk_iter, fuzzy_key, keys_obj, ip_stat); + }); + } + ucl_object_insert_key(obj, keys_obj, "keys", 0, false); /* Now generic stats */ diff --git a/src/libcryptobox/cryptobox.c b/src/libcryptobox/cryptobox.c index eeeed020c..a976653df 100644 --- a/src/libcryptobox/cryptobox.c +++ b/src/libcryptobox/cryptobox.c @@ -86,6 +86,60 @@ rspamd_cryptobox_cpuid(int cpu[4], int info) #endif } +#ifdef HAVE_BUILTIN_CPU_SUPPORTS +RSPAMD_CONSTRUCTOR(cryptobox_cpu_init) +{ + __builtin_cpu_init(); +} +static gboolean +rspamd_cryptobox_test_instr(int instr) +{ + gboolean ret = FALSE; + switch (instr) { +#if defined HAVE_SSE2 && defined(__x86_64__) + case CPUID_SSE2: + ret = __builtin_cpu_supports("sse2"); + break; + case CPUID_RDRAND: + /* XXX: no check to test for rdrand, but all avx2 cpus are def. capable of rdrand */ + ret = __builtin_cpu_supports("avx2"); + break; +#endif +#ifdef HAVE_SSE3 + case CPUID_SSE3: + ret = __builtin_cpu_supports("sse3"); + break; +#endif +#ifdef HAVE_SSSE3 + case CPUID_SSSE3: + ret = __builtin_cpu_supports("ssse3"); + break; +#endif +#ifdef HAVE_SSE41 + case CPUID_SSE41: + ret = __builtin_cpu_supports("sse4.1"); + break; +#endif +#if defined HAVE_SSE42 && defined(__x86_64__) + case CPUID_SSE42: + ret = __builtin_cpu_supports("sse4.2"); + break; +#endif +#ifdef HAVE_AVX + case CPUID_AVX: + ret = __builtin_cpu_supports("avx"); + break; +#endif +#ifdef HAVE_AVX2 + case CPUID_AVX2: + ret = __builtin_cpu_supports("avx2"); + break; +#endif + } + + return ret; +} +#else static sig_atomic_t ok = 0; static jmp_buf j; @@ -171,6 +225,7 @@ rspamd_cryptobox_test_instr(int instr) /* We actually never return here if SIGILL has been caught */ return ok == 1; } +#endif /* HAVE_BUILTIN_CPU_SUPPORTS */ struct rspamd_cryptobox_library_ctx * rspamd_cryptobox_init(void) diff --git a/src/libcryptobox/keypair.c b/src/libcryptobox/keypair.c index d3f81ee2d..3556e188f 100644 --- a/src/libcryptobox/keypair.c +++ b/src/libcryptobox/keypair.c @@ -34,7 +34,7 @@ rspamd_keypair_quark(void) */ static void * rspamd_cryptobox_keypair_sk(struct rspamd_cryptobox_keypair *kp, - unsigned int *len) + size_t *len) { g_assert(kp != NULL); @@ -50,7 +50,7 @@ rspamd_cryptobox_keypair_sk(struct rspamd_cryptobox_keypair *kp, static void * rspamd_cryptobox_keypair_pk(struct rspamd_cryptobox_keypair *kp, - unsigned int *len) + size_t *len) { g_assert(kp != NULL); @@ -66,7 +66,7 @@ rspamd_cryptobox_keypair_pk(struct rspamd_cryptobox_keypair *kp, static void * rspamd_cryptobox_pubkey_pk(const struct rspamd_cryptobox_pubkey *kp, - unsigned int *len) + size_t *len) { g_assert(kp != NULL); @@ -139,7 +139,7 @@ void rspamd_cryptobox_nm_dtor(struct rspamd_cryptobox_nm *nm) void rspamd_cryptobox_keypair_dtor(struct rspamd_cryptobox_keypair *kp) { void *sk; - unsigned int len = 0; + size_t len = 0; sk = rspamd_cryptobox_keypair_sk(kp, &len); g_assert(sk != NULL && len > 0); @@ -168,7 +168,7 @@ rspamd_keypair_new(enum rspamd_cryptobox_keypair_type type) { struct rspamd_cryptobox_keypair *kp; void *pk, *sk; - unsigned int size; + size_t size; kp = rspamd_cryptobox_keypair_alloc(type); kp->type = type; @@ -241,7 +241,7 @@ rspamd_pubkey_from_base32(const char *b32, { unsigned char *decoded; gsize dlen, expected_len; - unsigned int pklen; + size_t pklen; struct rspamd_cryptobox_pubkey *pk; unsigned char *pk_data; @@ -283,7 +283,7 @@ rspamd_pubkey_from_hex(const char *hex, { unsigned char *decoded; gsize dlen, expected_len; - unsigned int pklen; + size_t pklen; struct rspamd_cryptobox_pubkey *pk; unsigned char *pk_data; @@ -325,7 +325,7 @@ rspamd_pubkey_from_bin(const unsigned char *raw, gsize len, enum rspamd_cryptobox_keypair_type type) { - unsigned int pklen; + size_t pklen; struct rspamd_cryptobox_pubkey *pk; unsigned char *pk_data; @@ -421,7 +421,7 @@ rspamd_pubkey_get_pk(struct rspamd_cryptobox_pubkey *pk, unsigned int *len) { unsigned char *ret = NULL; - unsigned int rlen; + size_t rlen; ret = rspamd_cryptobox_pubkey_pk(pk, &rlen); @@ -434,7 +434,8 @@ rspamd_pubkey_get_pk(struct rspamd_cryptobox_pubkey *pk, static void rspamd_keypair_print_component(unsigned char *data, gsize datalen, - GString *res, unsigned int how, const char *description) + GString *res, unsigned int how, const char *description, + enum rspamd_cryptobox_keypair_encoding encoding) { int olen, b32_len; @@ -442,7 +443,7 @@ rspamd_keypair_print_component(unsigned char *data, gsize datalen, rspamd_printf_gstring(res, "%s: ", description); } - if (how & RSPAMD_KEYPAIR_BASE32) { + if (encoding == RSPAMD_KEYPAIR_ENCODING_ZBASE32) { b32_len = (datalen * 8 / 5) + 2; g_string_set_size(res, res->len + b32_len); res->len -= b32_len; @@ -454,9 +455,12 @@ rspamd_keypair_print_component(unsigned char *data, gsize datalen, res->str[res->len] = '\0'; } } - else if (how & RSPAMD_KEYPAIR_HEX) { + else if (encoding == RSPAMD_KEYPAIR_ENCODING_HEX) { rspamd_printf_gstring(res, "%*xs", (int) datalen, data); } + else if (encoding == RSPAMD_KEYPAIR_ENCODING_BASE64) { + rspamd_printf_gstring(res, "%*Bs", (int) datalen, data); + } else { g_string_append_len(res, data, datalen); } @@ -467,10 +471,12 @@ rspamd_keypair_print_component(unsigned char *data, gsize datalen, } GString * -rspamd_keypair_print(struct rspamd_cryptobox_keypair *kp, unsigned int how) +rspamd_keypair_print(struct rspamd_cryptobox_keypair *kp, + enum rspamd_cryptobox_keypair_encoding encoding, + unsigned int how) { GString *res; - unsigned int len; + size_t len; gpointer p; g_assert(kp != NULL); @@ -479,28 +485,30 @@ rspamd_keypair_print(struct rspamd_cryptobox_keypair *kp, unsigned int how) if ((how & RSPAMD_KEYPAIR_PUBKEY)) { p = rspamd_cryptobox_keypair_pk(kp, &len); - rspamd_keypair_print_component(p, len, res, how, "Public key"); + rspamd_keypair_print_component(p, len, res, how, "Public key", encoding); } if ((how & RSPAMD_KEYPAIR_PRIVKEY)) { p = rspamd_cryptobox_keypair_sk(kp, &len); - rspamd_keypair_print_component(p, len, res, how, "Private key"); + rspamd_keypair_print_component(p, len, res, how, "Private key", encoding); } if ((how & RSPAMD_KEYPAIR_ID_SHORT)) { rspamd_keypair_print_component(kp->id, RSPAMD_KEYPAIR_SHORT_ID_LEN, - res, how, "Short key ID"); + res, how, "Short key ID", encoding); } if ((how & RSPAMD_KEYPAIR_ID)) { - rspamd_keypair_print_component(kp->id, sizeof(kp->id), res, how, "Key ID"); + rspamd_keypair_print_component(kp->id, sizeof(kp->id), res, how, "Key ID", encoding); } return res; } GString * -rspamd_pubkey_print(struct rspamd_cryptobox_pubkey *pk, unsigned int how) +rspamd_pubkey_print(struct rspamd_cryptobox_pubkey *pk, + enum rspamd_cryptobox_keypair_encoding encoding, + unsigned int how) { GString *res; - unsigned int len; + size_t len; gpointer p; g_assert(pk != NULL); @@ -509,15 +517,15 @@ rspamd_pubkey_print(struct rspamd_cryptobox_pubkey *pk, unsigned int how) if ((how & RSPAMD_KEYPAIR_PUBKEY)) { p = rspamd_cryptobox_pubkey_pk(pk, &len); - rspamd_keypair_print_component(p, len, res, how, "Public key"); + rspamd_keypair_print_component(p, len, res, how, "Public key", encoding); } if ((how & RSPAMD_KEYPAIR_ID_SHORT)) { rspamd_keypair_print_component(pk->id, RSPAMD_KEYPAIR_SHORT_ID_LEN, - res, how, "Short key ID"); + res, how, "Short key ID", encoding); } if ((how & RSPAMD_KEYPAIR_ID)) { rspamd_keypair_print_component(pk->id, sizeof(pk->id), res, how, - "Key ID"); + "Key ID", encoding); } return res; @@ -527,7 +535,7 @@ const unsigned char * rspamd_keypair_component(struct rspamd_cryptobox_keypair *kp, unsigned int ncomp, unsigned int *len) { - unsigned int rlen = 0; + size_t rlen = 0; const unsigned char *ret = NULL; g_assert(kp != NULL); @@ -558,11 +566,11 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj) const ucl_object_t *privkey, *pubkey, *elt; const char *str; enum rspamd_cryptobox_keypair_type type = RSPAMD_KEYPAIR_KEX; - gboolean is_hex = FALSE; + enum rspamd_cryptobox_keypair_encoding encoding = RSPAMD_KEYPAIR_ENCODING_DEFAULT; struct rspamd_cryptobox_keypair *kp; - unsigned int len; - gsize ucl_len; - int dec_len; + size_t len; + size_t ucl_len; + size_t dec_len; gpointer target; if (ucl_object_type(obj) != UCL_OBJECT) { @@ -605,7 +613,10 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj) str = ucl_object_tostring(elt); if (g_ascii_strcasecmp(str, "hex") == 0) { - is_hex = TRUE; + encoding = RSPAMD_KEYPAIR_ENCODING_HEX; + } + else if (g_ascii_strcasecmp(str, "base64") == 0) { + encoding = RSPAMD_KEYPAIR_ENCODING_BASE64; } /* TODO: handle errors */ } @@ -618,13 +629,29 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj) target = rspamd_cryptobox_keypair_sk(kp, &len); str = ucl_object_tolstring(privkey, &ucl_len); - if (is_hex) { + switch (encoding) { + case RSPAMD_KEYPAIR_ENCODING_HEX: dec_len = rspamd_decode_hex_buf(str, ucl_len, target, len); - } - else { + break; + case RSPAMD_KEYPAIR_ENCODING_ZBASE32: dec_len = rspamd_decode_base32_buf(str, ucl_len, target, len, RSPAMD_BASE32_DEFAULT); + break; + case RSPAMD_KEYPAIR_ENCODING_BASE64: + dec_len = rspamd_cryptobox_base64_decode(str, ucl_len, target, &len); + break; + case RSPAMD_KEYPAIR_ENCODING_BINARY: + if (len >= ucl_len) { + memcpy(target, str, ucl_len); + dec_len = ucl_len; + } + else { + memcpy(target, str, len); + dec_len = len; + } + break; } + if (dec_len != (int) len) { rspamd_keypair_unref(kp); @@ -634,11 +661,26 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj) target = rspamd_cryptobox_keypair_pk(kp, &len); str = ucl_object_tolstring(pubkey, &ucl_len); - if (is_hex) { + switch (encoding) { + case RSPAMD_KEYPAIR_ENCODING_HEX: dec_len = rspamd_decode_hex_buf(str, ucl_len, target, len); - } - else { + break; + case RSPAMD_KEYPAIR_ENCODING_ZBASE32: dec_len = rspamd_decode_base32_buf(str, ucl_len, target, len, RSPAMD_BASE32_DEFAULT); + break; + case RSPAMD_KEYPAIR_ENCODING_BASE64: + dec_len = rspamd_cryptobox_base64_decode(str, ucl_len, target, &len); + break; + case RSPAMD_KEYPAIR_ENCODING_BINARY: + if (len >= ucl_len) { + memcpy(target, str, ucl_len); + dec_len = ucl_len; + } + else { + memcpy(target, str, len); + dec_len = len; + } + break; } if (dec_len != (int) len) { @@ -660,24 +702,15 @@ rspamd_keypair_from_ucl(const ucl_object_t *obj) ucl_object_t * rspamd_keypair_to_ucl(struct rspamd_cryptobox_keypair *kp, + enum rspamd_cryptobox_keypair_encoding encoding, enum rspamd_keypair_dump_flags flags) { ucl_object_t *ucl_out, *elt; - int how = 0; GString *keypair_out; - const char *encoding; + const char *encoding_str = NULL; g_assert(kp != NULL); - if (flags & RSPAMD_KEYPAIR_DUMP_HEX) { - how |= RSPAMD_KEYPAIR_HEX; - encoding = "hex"; - } - else { - how |= RSPAMD_KEYPAIR_BASE32; - encoding = "base32"; - } - if (flags & RSPAMD_KEYPAIR_DUMP_FLATTENED) { ucl_out = ucl_object_typed_new(UCL_OBJECT); elt = ucl_out; @@ -688,10 +721,17 @@ rspamd_keypair_to_ucl(struct rspamd_cryptobox_keypair *kp, ucl_object_insert_key(ucl_out, elt, "keypair", 0, false); } + if (encoding == RSPAMD_KEYPAIR_ENCODING_HEX) { + encoding_str = "hex"; + } + else if (encoding == RSPAMD_KEYPAIR_ENCODING_BASE64) { + encoding_str = "base64"; + } + /* XXX: maybe support binary here ? */ /* pubkey part */ - keypair_out = rspamd_keypair_print(kp, - RSPAMD_KEYPAIR_PUBKEY | how); + keypair_out = rspamd_keypair_print(kp, encoding, + RSPAMD_KEYPAIR_PUBKEY | flags); ucl_object_insert_key(elt, ucl_object_fromlstring(keypair_out->str, keypair_out->len), "pubkey", 0, false); @@ -699,8 +739,8 @@ rspamd_keypair_to_ucl(struct rspamd_cryptobox_keypair *kp, if (!(flags & RSPAMD_KEYPAIR_DUMP_NO_SECRET)) { /* privkey part */ - keypair_out = rspamd_keypair_print(kp, - RSPAMD_KEYPAIR_PRIVKEY | how); + keypair_out = rspamd_keypair_print(kp, encoding, + RSPAMD_KEYPAIR_PRIVKEY | flags); ucl_object_insert_key(elt, ucl_object_fromlstring(keypair_out->str, keypair_out->len), "privkey", 0, false); @@ -708,15 +748,18 @@ rspamd_keypair_to_ucl(struct rspamd_cryptobox_keypair *kp, } keypair_out = rspamd_keypair_print(kp, - RSPAMD_KEYPAIR_ID | how); + encoding, + RSPAMD_KEYPAIR_ID | flags); ucl_object_insert_key(elt, ucl_object_fromlstring(keypair_out->str, keypair_out->len), "id", 0, false); g_string_free(keypair_out, TRUE); - ucl_object_insert_key(elt, - ucl_object_fromstring(encoding), - "encoding", 0, false); + if (encoding_str) { + ucl_object_insert_key(elt, + ucl_object_fromstring(encoding_str), + "encoding", 0, false); + } ucl_object_insert_key(elt, ucl_object_fromstring("curve25519"), diff --git a/src/libcryptobox/keypair.h b/src/libcryptobox/keypair.h index 97b46cbf5..80f4c4bd7 100644 --- a/src/libcryptobox/keypair.h +++ b/src/libcryptobox/keypair.h @@ -181,29 +181,8 @@ const unsigned char *rspamd_pubkey_get_pk(struct rspamd_cryptobox_pubkey *pk, #define RSPAMD_KEYPAIR_ID 0x4 /** Print short key id */ #define RSPAMD_KEYPAIR_ID_SHORT 0x8 -/** Encode output with base 32 */ -#define RSPAMD_KEYPAIR_BASE32 0x10 /** Human readable output */ #define RSPAMD_KEYPAIR_HUMAN 0x20 -#define RSPAMD_KEYPAIR_HEX 0x40 - -/** - * Print keypair encoding it if needed - * @param key key to print - * @param how flags that specifies printing behaviour - * @return newly allocated string with keypair - */ -GString *rspamd_keypair_print(struct rspamd_cryptobox_keypair *kp, - unsigned int how); - -/** - * Print pubkey encoding it if needed - * @param key key to print - * @param how flags that specifies printing behaviour - * @return newly allocated string with keypair - */ -GString *rspamd_pubkey_print(struct rspamd_cryptobox_pubkey *pk, - unsigned int how); /** Get keypair pubkey ID */ #define RSPAMD_KEYPAIR_COMPONENT_ID 0 @@ -232,17 +211,45 @@ struct rspamd_cryptobox_keypair *rspamd_keypair_from_ucl(const ucl_object_t *obj enum rspamd_keypair_dump_flags { RSPAMD_KEYPAIR_DUMP_DEFAULT = 0, - RSPAMD_KEYPAIR_DUMP_HEX = 1u << 0u, RSPAMD_KEYPAIR_DUMP_NO_SECRET = 1u << 1u, RSPAMD_KEYPAIR_DUMP_FLATTENED = 1u << 2u, }; +enum rspamd_cryptobox_keypair_encoding { + RSPAMD_KEYPAIR_ENCODING_ZBASE32 = 0, + RSPAMD_KEYPAIR_ENCODING_HEX = 1, + RSPAMD_KEYPAIR_ENCODING_BASE64 = 2, + RSPAMD_KEYPAIR_ENCODING_BINARY = 3, + RSPAMD_KEYPAIR_ENCODING_DEFAULT = RSPAMD_KEYPAIR_ENCODING_ZBASE32, +}; + + +/** + * Print pubkey encoding it if needed + * @param key key to print + * @param how flags that specifies printing behaviour + * @return newly allocated string with keypair + */ +GString *rspamd_pubkey_print(struct rspamd_cryptobox_pubkey *pk, + enum rspamd_cryptobox_keypair_encoding encoding, + unsigned int how); +/** + * Print keypair encoding it if needed + * @param key key to print + * @param how flags that specifies printing behaviour + * @return newly allocated string with keypair + */ +GString *rspamd_keypair_print(struct rspamd_cryptobox_keypair *kp, + enum rspamd_cryptobox_keypair_encoding encoding, + unsigned int how); /** * Converts keypair to ucl object * @param kp * @return */ + ucl_object_t *rspamd_keypair_to_ucl(struct rspamd_cryptobox_keypair *kp, + enum rspamd_cryptobox_keypair_encoding encoding, enum rspamd_keypair_dump_flags flags); diff --git a/src/libserver/http/http_connection.c b/src/libserver/http/http_connection.c index 1ae9bb034..baf37a385 100644 --- a/src/libserver/http/http_connection.c +++ b/src/libserver/http/http_connection.c @@ -1987,9 +1987,11 @@ int rspamd_http_message_write_header(const char *mime_type, gboolean encrypted, GString *b32_key, *b32_id; b32_key = rspamd_keypair_print(priv->local_key, - RSPAMD_KEYPAIR_PUBKEY | RSPAMD_KEYPAIR_BASE32); + RSPAMD_KEYPAIR_ENCODING_DEFAULT, + RSPAMD_KEYPAIR_PUBKEY); b32_id = rspamd_pubkey_print(peer_key, - RSPAMD_KEYPAIR_ID_SHORT | RSPAMD_KEYPAIR_BASE32); + RSPAMD_KEYPAIR_ENCODING_DEFAULT, + RSPAMD_KEYPAIR_ID_SHORT); /* XXX: add some fuzz here */ rspamd_printf_fstring(&*buf, "Key: %v=%v\r\n", b32_id, b32_key); g_string_free(b32_key, TRUE); diff --git a/src/libserver/maps/map.c b/src/libserver/maps/map.c index 631455755..43a9d5d86 100644 --- a/src/libserver/maps/map.c +++ b/src/libserver/maps/map.c @@ -685,7 +685,8 @@ rspamd_map_check_sig_pk_mem(const unsigned char *sig, if (ret) { b32_key = rspamd_pubkey_print(pk, - RSPAMD_KEYPAIR_BASE32 | RSPAMD_KEYPAIR_PUBKEY); + RSPAMD_KEYPAIR_ENCODING_DEFAULT, + RSPAMD_KEYPAIR_PUBKEY); msg_info_map("verified signature for %s using trusted key %v", map->name, b32_key); g_string_free(b32_key, TRUE); @@ -728,7 +729,8 @@ rspamd_map_check_file_sig(const char *fname, /* We just check pk against the trusted db of keys */ b32_key = rspamd_pubkey_print(pk, - RSPAMD_KEYPAIR_BASE32 | RSPAMD_KEYPAIR_PUBKEY); + RSPAMD_KEYPAIR_ENCODING_DEFAULT, + RSPAMD_KEYPAIR_PUBKEY); g_assert(b32_key != NULL); if (g_hash_table_lookup(map->cfg->trusted_keys, b32_key->str) == NULL) { diff --git a/src/lua/lua_cryptobox.c b/src/lua/lua_cryptobox.c index c9cac1562..9600a4732 100644 --- a/src/lua/lua_cryptobox.c +++ b/src/lua/lua_cryptobox.c @@ -503,7 +503,7 @@ lua_cryptobox_keypair_gc(lua_State *L) } /*** - * @method keypair:totable([hex=false]]) + * @method keypair:totable([encoding="zbase32"]) * Converts keypair to table (not very safe due to memory leftovers) */ static int @@ -512,16 +512,39 @@ lua_cryptobox_keypair_totable(lua_State *L) LUA_TRACE_POINT; struct rspamd_cryptobox_keypair *kp = lua_check_cryptobox_keypair(L, 1); ucl_object_t *obj; - gboolean hex = FALSE; + enum rspamd_cryptobox_keypair_encoding encoding = RSPAMD_KEYPAIR_ENCODING_DEFAULT; int ret = 1; if (kp != NULL) { if (lua_isboolean(L, 2)) { - hex = lua_toboolean(L, 2); + if (lua_toboolean(L, 2)) { + encoding = RSPAMD_KEYPAIR_ENCODING_HEX; + } } + else if (lua_isstring(L, 2)) { + const char *enc = lua_tostring(L, 2); - obj = rspamd_keypair_to_ucl(kp, hex ? RSPAMD_KEYPAIR_DUMP_HEX : RSPAMD_KEYPAIR_DUMP_DEFAULT); + if (g_ascii_strcasecmp(enc, "hex") == 0) { + encoding = RSPAMD_KEYPAIR_ENCODING_HEX; + } + else if (g_ascii_strcasecmp(enc, "zbase32") == 0 || + g_ascii_strcasecmp(enc, "default") == 0 || + g_ascii_strcasecmp(enc, "base32") == 0) { + encoding = RSPAMD_KEYPAIR_ENCODING_ZBASE32; + } + else if (g_ascii_strcasecmp(enc, "base64") == 0) { + encoding = RSPAMD_KEYPAIR_ENCODING_BASE64; + } + else if (g_ascii_strcasecmp(enc, "binary") == 0) { + encoding = RSPAMD_KEYPAIR_ENCODING_BINARY; + } + else { + return luaL_error(L, "unknown encoding (known are: hex, zbase32/default, base64, binary: %s", enc); + } + } + + obj = rspamd_keypair_to_ucl(kp, encoding, RSPAMD_KEYPAIR_DUMP_DEFAULT); ret = ucl_object_push_lua(L, obj, true); ucl_object_unref(obj); @@ -1415,7 +1438,11 @@ lua_cryptobox_hash_reset(lua_State *L) rspamd_cryptobox_hash_init(h->content.h, NULL, 0); break; case LUA_CRYPTOBOX_HASH_SSL: +#if OPENSSL_VERSION_MAJOR >= 3 EVP_DigestInit(h->content.c, EVP_MD_CTX_get0_md(h->content.c)); +#else + EVP_DigestInit(h->content.c, EVP_MD_CTX_md(h->content.c)); +#endif break; case LUA_CRYPTOBOX_HASH_HMAC: #if OPENSSL_VERSION_NUMBER < 0x10100000L || \ @@ -2508,31 +2535,20 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L) } if (strcmp(alg_str, "rsa") == 0) { - BIGNUM *e; EVP_PKEY *pk; - e = BN_new(); pk = EVP_PKEY_new(); - if (BN_set_word(e, RSA_F4) != 1) { - BN_free(e); - EVP_PKEY_free(pk); - - return luaL_error(L, "BN_set_word failed"); - } EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); if (EVP_PKEY_keygen_init(pctx) != 1) { - BN_free(e); EVP_PKEY_free(pk); EVP_PKEY_CTX_free(pctx); return luaL_error(L, "EVP_PKEY_keygen_init failed"); } EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, nbits); - EVP_PKEY_CTX_set1_rsa_keygen_pubexp(pctx, e); if (EVP_PKEY_keygen(pctx, &pk) != 1) { - BN_free(e); EVP_PKEY_free(pk); EVP_PKEY_CTX_free(pctx); @@ -2552,7 +2568,6 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L) if (rc == 0) { BIO_free(mbio); - BN_free(e); EVP_PKEY_free(pk); return luaL_error(L, "i2d_RSAPrivateKey_bio failed"); @@ -2574,7 +2589,6 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L) if (rc == 0) { BIO_free(mbio); - BN_free(e); EVP_PKEY_free(pk); return luaL_error(L, "i2d_RSA_PUBKEY_bio failed"); @@ -2590,7 +2604,6 @@ lua_cryptobox_gen_dkim_keypair(lua_State *L) pub_out->len = b64_len; pub_out->flags = RSPAMD_TEXT_FLAG_OWN; - BN_free(e); EVP_PKEY_free(pk); BIO_free(mbio); } diff --git a/src/lua/lua_map.c b/src/lua/lua_map.c index 1cc2ce1bd..062613bd7 100644 --- a/src/lua/lua_map.c +++ b/src/lua/lua_map.c @@ -1256,8 +1256,8 @@ lua_map_get_sign_key(lua_State *L) bk = g_ptr_array_index(map->map->backends, i); if (bk->trusted_pubkey) { - ret = rspamd_pubkey_print(bk->trusted_pubkey, - RSPAMD_KEYPAIR_PUBKEY | RSPAMD_KEYPAIR_BASE32); + ret = rspamd_pubkey_print(bk->trusted_pubkey, RSPAMD_KEYPAIR_ENCODING_DEFAULT, + RSPAMD_KEYPAIR_PUBKEY); } else { ret = NULL; diff --git a/src/lua/lua_rsa.c b/src/lua/lua_rsa.c index b7be612b0..4b9aa0354 100644 --- a/src/lua/lua_rsa.c +++ b/src/lua/lua_rsa.c @@ -261,6 +261,7 @@ lua_rsa_pubkey_gc(lua_State *L) EVP_PKEY *pkey = lua_check_rsa_pubkey(L, 1); if (pkey != NULL) { + /* It's actually EVP_PKEY_unref, thanks for that API */ EVP_PKEY_free(pkey); } @@ -522,6 +523,7 @@ lua_rsa_privkey_gc(lua_State *L) EVP_PKEY *pkey = lua_check_rsa_privkey(L, 1); if (pkey != NULL) { + /* It's actually EVP_PKEY_unref, thanks for that API */ EVP_PKEY_free(pkey); } @@ -758,7 +760,7 @@ lua_rsa_sign_memory(lua_State *L) data = luaL_checklstring(L, 2, &sz); if (pkey != NULL && data != NULL) { - signature = rspamd_fstring_sized_new(EVP_PKEY_get_size(pkey)); + signature = rspamd_fstring_sized_new(EVP_PKEY_size(pkey)); EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pkey, NULL); g_assert(pctx != NULL); @@ -791,7 +793,6 @@ lua_rsa_sign_memory(lua_State *L) static int lua_rsa_keypair(lua_State *L) { - BIGNUM *e; EVP_PKEY *pkey = NULL, *pub_pkey, *priv_pkey, **ppkey; int bits = lua_gettop(L) > 0 ? lua_tointeger(L, 1) : 1024; @@ -799,32 +800,31 @@ lua_rsa_keypair(lua_State *L) return luaL_error(L, "invalid bits count"); } - e = BN_new(); - - g_assert(BN_set_word(e, RSA_F4) == 1); EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); g_assert(pctx != NULL); g_assert(EVP_PKEY_keygen_init(pctx) == 1); g_assert(EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, bits) == 1); - g_assert(EVP_PKEY_CTX_set1_rsa_keygen_pubexp(pctx, e) == 1); - g_assert(EVP_PKEY_keygen(pctx, &pkey) == 1); g_assert(pkey != NULL); - priv_pkey = EVP_PKEY_dup(pkey); + /* Increase refcount and share */ + g_assert(EVP_PKEY_up_ref(pkey) == 1); + priv_pkey = pkey; + ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *)); rspamd_lua_setclass(L, rspamd_rsa_privkey_classname, -1); *ppkey = priv_pkey; - pub_pkey = EVP_PKEY_dup(pkey); + /* Increase refcount and share */ + g_assert(EVP_PKEY_up_ref(pkey) == 1); + pub_pkey = pkey; ppkey = lua_newuserdata(L, sizeof(EVP_PKEY *)); rspamd_lua_setclass(L, rspamd_rsa_pubkey_classname, -1); *ppkey = pub_pkey; EVP_PKEY_free(pkey); EVP_PKEY_CTX_free(pctx); - BN_free(e); return 2; } diff --git a/src/plugins/lua/dmarc.lua b/src/plugins/lua/dmarc.lua index 792672bd0..28b3db867 100644 --- a/src/plugins/lua/dmarc.lua +++ b/src/plugins/lua/dmarc.lua @@ -264,6 +264,13 @@ local function dmarc_validate_policy(task, policy, hdrfromdom, dmarc_esld) end if policy.rua and redis_params and settings.reporting.enabled then + if settings.reporting.only_domains then + if not (settings.reporting.only_domains:get_key(policy.domain) or + settings.reporting.only_domains:get_key(rspamd_util.get_tld(policy.domain))) then + rspamd_logger.info(task, 'DMARC reporting suppressed for sender domain %s', policy.domain) + return + end + end if settings.reporting.exclude_domains then if settings.reporting.exclude_domains:get_key(policy.domain) or settings.reporting.exclude_domains:get_key(rspamd_util.get_tld(policy.domain)) then @@ -527,6 +534,11 @@ if type(settings.reporting) == 'table' then type = 'map', description = 'Recipients not to store DMARC reports for' }, + only_domains = { + optional = true, + type = 'map', + description = 'Domains to store DMARC reports about' + }, }) end diff --git a/src/rspamadm/signtool.c b/src/rspamadm/signtool.c index ddc3d45df..6d60e6700 100644 --- a/src/rspamadm/signtool.c +++ b/src/rspamadm/signtool.c @@ -366,7 +366,8 @@ rspamadm_sign_file(const char *fname, struct rspamd_cryptobox_keypair *kp) } else { b32_pk = rspamd_keypair_print(kp, - RSPAMD_KEYPAIR_PUBKEY | RSPAMD_KEYPAIR_BASE32); + RSPAMD_KEYPAIR_ENCODING_DEFAULT, + RSPAMD_KEYPAIR_PUBKEY); if (b32_pk) { rspamd_fprintf(pub_fp, "%v", b32_pk); diff --git a/test/functional/cases/150_rspamadm.robot b/test/functional/cases/150_rspamadm.robot index 6bff14b2e..257b0b501 100644 --- a/test/functional/cases/150_rspamadm.robot +++ b/test/functional/cases/150_rspamadm.robot @@ -4,6 +4,13 @@ Suite Teardown Rspamadm Teardown Library ${RSPAMD_TESTDIR}/lib/rspamd.py Resource ${RSPAMD_TESTDIR}/lib/rspamd.robot +*** Variables *** +${TEXT} text +${KEY} 12345678901234567890123456789012 +${NONCE} 9pyeEd986hrjcpozCIZ41jEo6dCDbgjg +${ENCRYPTED_TEXT} 8KGF6VLI7vnweUdR8FuQZuT+ID8= +${PYTHON_SCRIPT} ${RSPAMD_TESTDIR}/../../utils/encrypt_decrypt_header.py + *** Test Cases *** Config Test ${result} = Rspamadm configtest @@ -46,3 +53,43 @@ Verbose mode Should Match Regexp ${result.stderr} ^$ Should Match Regexp ${result.stdout} hello world\n Should Be Equal As Integers ${result.rc} 0 + +SecretBox rspamadm encrypt/decrypt + ${result} = Rspamadm secret_box -B encrypt -t ${TEXT} -k ${KEY} -n ${NONCE} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${result.stdout} ${NONCE}${ENCRYPTED_TEXT} + ${result1} = Rspamadm secret_box -B decrypt -t ${ENCRYPTED_TEXT} -k ${KEY} -n ${NONCE} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${result1.stdout} ${TEXT} + +SecretBox python encrypt/decrypt + ${result} = Run Process python3 ${PYTHON_SCRIPT} -B encrypt -t ${TEXT} -k ${KEY} -n ${NONCE} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${result.stdout} ${NONCE}${ENCRYPTED_TEXT} + ${result1} = Run Process python3 ${PYTHON_SCRIPT} -B decrypt -t ${NONCE}${ENCRYPTED_TEXT} -k ${KEY} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${result1.stdout} ${TEXT} + +SecretBox encrypt python with nonce decrypt rspamadm + ${result} = Run Process python3 ${PYTHON_SCRIPT} -B encrypt -t ${TEXT} -k ${KEY} -n ${NONCE} + ${result1} = Rspamadm secret_box -B decrypt -t ${result.stdout} -k ${KEY} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${TEXT} ${result1.stdout} + +SecretBox encrypt python without nonce decrypt rspamadm + ${result} = Run Process python3 ${PYTHON_SCRIPT} -B encrypt -t ${TEXT} -k ${KEY} + ${result1} = Rspamadm secret_box -B decrypt -t ${result.stdout} -k ${KEY} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${TEXT} ${result1.stdout} + +SecretBox encrypt rspamadm with nonce decrypt python + ${result} = Rspamadm secret_box -B encrypt -t ${TEXT} -k ${KEY} -n ${NONCE} + ${result1} = Run Process python3 ${PYTHON_SCRIPT} -B decrypt -t ${result.stdout} -k ${KEY} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${TEXT} ${result1.stdout} + +SecretBox encrypt rspamadm without nonce decrypt python + ${result} = Rspamadm secret_box -B encrypt -t ${TEXT} -k ${KEY} + ${result1} = Run Process python3 ${PYTHON_SCRIPT} -B decrypt -t ${result.stdout} -k ${KEY} + Should Match Regexp ${result.stderr} ^$ + Should Be Equal As Strings ${TEXT} ${result1.stdout} diff --git a/test/lua/unit/lua_util.maybe_encrypt_decrypt_header.lua b/test/lua/unit/lua_util.maybe_encrypt_decrypt_header.lua index 613101068..ef31f5e9b 100644 --- a/test/lua/unit/lua_util.maybe_encrypt_decrypt_header.lua +++ b/test/lua/unit/lua_util.maybe_encrypt_decrypt_header.lua @@ -15,7 +15,8 @@ context("Lua util - maybe encrypt/decrypt header", function() assert_true(false, 'Failed to encrypt header') end - local decrypted_header = util.maybe_decrypt_header(encrypted_header, settings, settings.prefix) + local text = string.sub(tostring(encrypted_header), 6) + local decrypted_header = util.maybe_decrypt_header(text, settings, settings.prefix) if decrypted_header == encrypted_header or decrypted_header == nil then assert_true(false, 'Failed to decrypt header') end @@ -36,13 +37,15 @@ context("Lua util - maybe encrypt/decrypt header", function() prefix_key = 'key' } - local encrypted_header, nonce = util.maybe_encrypt_header(header, settings, settings.prefix) + local encrypted_header = util.maybe_encrypt_header(header, settings, settings.prefix) if encrypted_header == header or encrypted_header == nil then assert_true(false, 'Failed to encrypt header') end - local decrypted_header = util.maybe_decrypt_header(encrypted_header, settings, - settings.prefix, nonce) + local nonce = string.sub(tostring(encrypted_header), 1, 24) + local text = string.sub(tostring(encrypted_header), 25) + local decrypted_header = util.maybe_decrypt_header(text, settings, settings.prefix, nonce) + if decrypted_header == encrypted_header or decrypted_header == nil then assert_true(false, 'Failed to decrypt header') end diff --git a/utils/encrypt_decrypt_header.py b/utils/encrypt_decrypt_header.py new file mode 100644 index 000000000..5f2ea755e --- /dev/null +++ b/utils/encrypt_decrypt_header.py @@ -0,0 +1,97 @@ +import argparse +import base64 + +import nacl.encoding +from nacl.secret import SecretBox +from nacl.hash import blake2b + + +def create_secret_box(key): + key = blake2b(key, encoder=nacl.encoding.RawEncoder) + box = SecretBox(key) + return box + +def encrypt_text(header, key, nonce): + box = create_secret_box(key) + if nonce is not None: + encrypted_header = box.encrypt(header, nonce=nonce) + else: + encrypted_header = box.encrypt(header) + return encrypted_header + +def decrypt_text(encrypted_header, key): + box = create_secret_box(key) + decrypted_header = box.decrypt(encrypted_header) + return decrypted_header + +def set_encoding(args, type_, text): + output = text + if type_ == 'encode': + if args.hex: + output = base64.b16encode(text) + elif args.base32: + output = base64.b32encode(text) + elif args.base64: + output = base64.b64encode(text) + elif type_ == 'decode': + if args.hex: + output = base64.b16decode(text) + elif args.base32: + output = base64.b32decode(text) + elif args.base64: + output = base64.b64decode(text) + return output + +def set_up_parser_args(): + new_parser = argparse.ArgumentParser(description="Encrypt or Decrypt a text.") + enc_group = new_parser.add_mutually_exclusive_group() + + enc_group.add_argument("-r", "--raw", action="store_true", + help="Encrypted text(and nonce if it is there) will be given in raw") + enc_group.add_argument("-H", "--hex", action="store_true", + help="Encrypted text(and nonce if it is there) will be given in hex") + enc_group.add_argument("-b", "--base32", action="store_true", + help="Encrypted text(and nonce if it is there) will be given in base32") + enc_group.add_argument("-B", "--base64", action="store_true", + help="Encrypted text(and nonce if it is there) will be given in base64") + + subparsers = new_parser.add_subparsers(dest="command", help="encrypt or decrypt") + + encrypt_parser = subparsers.add_parser("encrypt", help="Encrypt a text") + encrypt_parser.add_argument("-t", "--text", type=str, required=True, help="Text to encrypt") + encrypt_parser.add_argument("-k", "--key", type=str, required=True, help="Encryption key") + encrypt_parser.add_argument("-n", "--nonce", type=str, required=False, help="Encryption nonce") + + decrypt_parser = subparsers.add_parser("decrypt", help="Decrypt a text") + decrypt_parser.add_argument("-t", "--encrypted_text", type=str, required=True, help="Encrypted text") + decrypt_parser.add_argument("-k", "--key", type=str, required=True, help="Decryption key") + + args = new_parser.parse_args() + return args + +def main(): + args = set_up_parser_args() + + if args.command == "encrypt": + text = args.text.encode() + key = args.key.encode() + if args.nonce is not None: + nonce = set_encoding(args, 'decode', args.nonce) + else: + nonce = None + + encrypted_text = encrypt_text(text, key, nonce) + if args.raw: + print(set_encoding(args, 'encode', encrypted_text)) + else: + print(set_encoding(args, 'encode', encrypted_text).decode()) + + elif args.command == "decrypt": + encrypted_text = set_encoding(args, 'decode', args.encrypted_text) + key = args.key.encode() + + decrypted_text = decrypt_text(encrypted_text, key) + print(decrypted_text.decode()) + +if __name__ == "__main__": + main()
\ No newline at end of file |