From c5a798beec0f24f6828cd3c43bbefd1e18a9d33a Mon Sep 17 00:00:00 2001 From: Bakasura Date: Mon, 13 Jul 2020 18:27:27 -0500 Subject: FiSHLiM: Support for CBC mode + more commands (#2347) --- plugins/fishlim/tests/fake/keystore.c | 47 +++++ plugins/fishlim/tests/meson.build | 17 ++ plugins/fishlim/tests/old_version/fish.c | 194 ++++++++++++++++++++ plugins/fishlim/tests/old_version/fish.h | 39 ++++ plugins/fishlim/tests/old_version/meson.build | 4 + plugins/fishlim/tests/tests.c | 248 ++++++++++++++++++++++++++ 6 files changed, 549 insertions(+) create mode 100644 plugins/fishlim/tests/fake/keystore.c create mode 100644 plugins/fishlim/tests/meson.build create mode 100644 plugins/fishlim/tests/old_version/fish.c create mode 100644 plugins/fishlim/tests/old_version/fish.h create mode 100644 plugins/fishlim/tests/old_version/meson.build create mode 100644 plugins/fishlim/tests/tests.c (limited to 'plugins/fishlim/tests') diff --git a/plugins/fishlim/tests/fake/keystore.c b/plugins/fishlim/tests/fake/keystore.c new file mode 100644 index 00000000..855b3451 --- /dev/null +++ b/plugins/fishlim/tests/fake/keystore.c @@ -0,0 +1,47 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include "../../fish.h" + + +/** + * Extracts a key from the key store file. + */ +char *keystore_get_key(const char *nick, enum fish_mode *mode) { + return NULL; +} + +/** + * Sets a key in the key store file. + */ +gboolean keystore_store_key(const char *nick, const char *key, enum fish_mode mode) { + return NULL; +} + +/** + * Deletes a nick from the key store. + */ +gboolean keystore_delete_nick(const char *nick) { + return NULL; +} diff --git a/plugins/fishlim/tests/meson.build b/plugins/fishlim/tests/meson.build new file mode 100644 index 00000000..3b75018e --- /dev/null +++ b/plugins/fishlim/tests/meson.build @@ -0,0 +1,17 @@ +subdir('old_version') + +fishlim_test_sources = [ + 'tests.c', + 'fake/keystore.c', + '../fish.c', + '../utils.c', +] + +fishlim_tests = executable('fishlim_tests', fishlim_test_sources, + dependencies: [libgio_dep, libssl_dep], + link_with : fishlim_old_lib +) + +test('Fishlim Tests', fishlim_tests, + timeout: 90 +) diff --git a/plugins/fishlim/tests/old_version/fish.c b/plugins/fishlim/tests/old_version/fish.c new file mode 100644 index 00000000..ff0b8fea --- /dev/null +++ b/plugins/fishlim/tests/old_version/fish.c @@ -0,0 +1,194 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifdef __APPLE__ +#define __AVAILABILITYMACROS__ +#define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER +#endif + +#include +#include +#include + +#include "fish.h" + +#define IB 64 +static const char fish_base64[64] = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static const signed char fish_unbase64[256] = { + IB,IB,IB,IB,IB,IB,IB,IB, IB,IB,IB,IB,IB,IB,IB,IB, + IB,IB,IB,IB,IB,IB,IB,IB, IB,IB,IB,IB,IB,IB,IB,IB, +/* ! " # $ % & ' ( ) * + , - . / */ + IB,IB,IB,IB,IB,IB,IB,IB, IB,IB,IB,IB,IB,IB, 0, 1, +/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + 2, 3, 4, 5, 6, 7, 8, 9, 10,11,IB,IB,IB,IB,IB,IB, +/* @ A B C D E F G H I J K L M N O */ + IB,38,39,40,41,42,43,44, 45,46,47,48,49,50,51,52, +/* P Q R S T U V W X Y Z [ \ ] ^ _*/ + 53,54,55,56,57,58,59,60, 61,62,63,IB,IB,IB,IB,IB, +/* ` a b c d e f g h i j k l m n o */ + IB,12,13,14,15,16,17,18, 19,20,21,22,23,24,25,26, +/* p q r s t u v w x y z { | } ~ */ + 27,28,29,30,31,32,33,34, 35,36,37,IB,IB,IB,IB,IB, +}; + +#define GET_BYTES(dest, source) do { \ + *((dest)++) = ((source) >> 24) & 0xFF; \ + *((dest)++) = ((source) >> 16) & 0xFF; \ + *((dest)++) = ((source) >> 8) & 0xFF; \ + *((dest)++) = (source) & 0xFF; \ +} while (0); + + +char *__old_fish_encrypt(const char *key, size_t keylen, const char *message) { + BF_KEY bfkey; + size_t messagelen; + size_t i; + int j; + char *encrypted; + char *end; + unsigned char bit; + unsigned char word; + unsigned char d; + BF_set_key(&bfkey, keylen, (const unsigned char*)key); + + messagelen = strlen(message); + if (messagelen == 0) return NULL; + encrypted = g_malloc(((messagelen - 1) / 8) * 12 + 12 + 1); /* each 8-byte block becomes 12 bytes */ + end = encrypted; + + while (*message) { + /* Read 8 bytes (a Blowfish block) */ + BF_LONG binary[2] = { 0, 0 }; + unsigned char c; + for (i = 0; i < 8; i++) { + c = message[i]; + binary[i >> 2] |= c << 8*(3 - (i&3)); + if (c == '\0') break; + } + message += 8; + + /* Encrypt block */ + BF_encrypt(binary, &bfkey); + + /* Emit FiSH-BASE64 */ + bit = 0; + word = 1; + for (j = 0; j < 12; j++) { + d = fish_base64[(binary[word] >> bit) & 63]; + *(end++) = d; + bit += 6; + if (j == 5) { + bit = 0; + word = 0; + } + } + + /* Stop if a null terminator was found */ + if (c == '\0') break; + } + *end = '\0'; + return encrypted; +} + + +char *__old_fish_decrypt(const char *key, size_t keylen, const char *data) { + BF_KEY bfkey; + size_t i; + char *decrypted; + char *end; + unsigned char bit; + unsigned char word; + unsigned char d; + BF_set_key(&bfkey, keylen, (const unsigned char*)key); + + decrypted = g_malloc(strlen(data) + 1); + end = decrypted; + + while (*data) { + /* Convert from FiSH-BASE64 */ + BF_LONG binary[2] = { 0, 0 }; + bit = 0; + word = 1; + for (i = 0; i < 12; i++) { + d = fish_unbase64[(const unsigned char)*(data++)]; + if (d == IB) goto decrypt_end; + binary[word] |= (unsigned long)d << bit; + bit += 6; + if (i == 5) { + bit = 0; + word = 0; + } + } + + /* Decrypt block */ + BF_decrypt(binary, &bfkey); + + /* Copy to buffer */ + GET_BYTES(end, binary[0]); + GET_BYTES(end, binary[1]); + } + + decrypt_end: + *end = '\0'; + return decrypted; +} + +/** + * Encrypts a message (see __old_fish_decrypt). The key is searched for in the + * key store. + */ +char *__old_fish_encrypt_for_nick(const char *nick, const char *data) { + char *key; + char *encrypted; + + /* Look for key */ + /*key = keystore_get_key(nick);*/ + if (!key) return NULL; + + /* Encrypt */ + encrypted = __old_fish_encrypt(key, strlen(key), data); + + g_free(key); + return encrypted; +} + +/** + * Decrypts a message (see __old_fish_decrypt). The key is searched for in the + * key store. + */ +char *__old_fish_decrypt_from_nick(const char *nick, const char *data) { + char *key; + char *decrypted; + /* Look for key */ + /*key = keystore_get_key(nick);*/ + if (!key) return NULL; + + /* Decrypt */ + decrypted = __old_fish_decrypt(key, strlen(key), data); + + g_free(key); + return decrypted; +} + + diff --git a/plugins/fishlim/tests/old_version/fish.h b/plugins/fishlim/tests/old_version/fish.h new file mode 100644 index 00000000..b4c66572 --- /dev/null +++ b/plugins/fishlim/tests/old_version/fish.h @@ -0,0 +1,39 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef FISH_OLD_H +#define FISH_OLD_H + +#include + +#include + +char *__old_fish_encrypt(const char *key, size_t keylen, const char *message); +char *__old_fish_decrypt(const char *key, size_t keylen, const char *data); +char *__old_fish_encrypt_for_nick(const char *nick, const char *data); +char *__old_fish_decrypt_from_nick(const char *nick, const char *data); + +#endif + + diff --git a/plugins/fishlim/tests/old_version/meson.build b/plugins/fishlim/tests/old_version/meson.build new file mode 100644 index 00000000..54647d71 --- /dev/null +++ b/plugins/fishlim/tests/old_version/meson.build @@ -0,0 +1,4 @@ +fishlim_old_lib = shared_library('fishlim_old_lib', + ['fish.c'], + dependencies: [libgio_dep, libssl_dep], +) diff --git a/plugins/fishlim/tests/tests.c b/plugins/fishlim/tests/tests.c new file mode 100644 index 00000000..e34532bf --- /dev/null +++ b/plugins/fishlim/tests/tests.c @@ -0,0 +1,248 @@ +/* + + Copyright (c) 2020 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef PLUGIN_HEXCHAT_FISHLIM_TEST_H +#define PLUGIN_HEXCHAT_FISHLIM_TEST_H + +// Libs +#include +#include +// Project Libs +#include "../fish.h" +#include "../utils.h" +#include "old_version/fish.h" + +/** + * Auxiliary function: Generate a random string + * @param out Preallocated string to fill + * @param len Size of bytes to fill + */ +void random_string(char *out, size_t len) { + GRand *rand = NULL; + int i = 0; + + rand = g_rand_new(); + for (i = 0; i < len; ++i) { + out[i] = g_rand_int_range(rand, 1, 256); + } + + out[len] = 0; + + g_rand_free(rand); +} + + +/** + * Check encrypt and decrypt in ECB mode and compare with old implementation + */ +void __ecb() { + char *bo64 = NULL, *b64 = NULL; + char *deo = NULL, *de = NULL; + int key_len, message_len = 0; + char key[57]; + char message[1000]; + + /* Generate key 32–448 bits (Yes, I start with 8 bits) */ + for (key_len = 1; key_len < 57; ++key_len) { + + random_string(key, key_len); + + for (message_len = 1; message_len < 1000; ++message_len) { + random_string(message, message_len); + + /* Encrypt */ + bo64 = __old_fish_encrypt(key, key_len, message); + g_assert_nonnull(bo64); + b64 = fish_encrypt(key, key_len, message, message_len, FISH_ECB_MODE); + g_assert_nonnull(b64); + g_assert_cmpuint(g_strcmp0(b64, bo64), == , 0); + + /* Decrypt */ + /* Linear */ + deo = __old_fish_decrypt(key, key_len, bo64); + de = fish_decrypt_str(key, key_len, b64, FISH_ECB_MODE); + g_assert_nonnull(deo); + g_assert_nonnull(de); + g_assert_cmpuint(g_strcmp0(de, message), == , 0); + g_assert_cmpuint(g_strcmp0(deo, message), == , 0); + g_assert_cmpuint(g_strcmp0(de, deo), == , 0); + g_free(deo); + g_free(de); + /* Mixed */ + deo = __old_fish_decrypt(key, key_len, b64); + de = fish_decrypt_str(key, key_len, bo64, FISH_ECB_MODE); + g_assert_nonnull(deo); + g_assert_nonnull(de); + g_assert_cmpuint(g_strcmp0(de, message), == , 0); + g_assert_cmpuint(g_strcmp0(deo, message), == , 0); + g_assert_cmpuint(g_strcmp0(de, deo), == , 0); + g_free(deo); + g_free(de); + + /* Free */ + g_free(bo64); + g_free(b64); + } + } +} + +/** + * Check encrypt and decrypt in CBC mode + */ +void __cbc() { + char *b64 = NULL; + char *de = NULL; + int key_len, message_len = 0; + char key[57]; + char message[1000]; + + /* Generate key 32–448 bits (Yes, I start with 8 bits) */ + for (key_len = 1; key_len < 57; ++key_len) { + + random_string(key, key_len); + + for (message_len = 1; message_len < 1000; ++message_len) { + random_string(message, message_len); + + /* Encrypt */ + b64 = fish_encrypt(key, key_len, message, message_len, FISH_CBC_MODE); + g_assert_nonnull(b64); + + /* Decrypt */ + /* Linear */ + de = fish_decrypt_str(key, key_len, b64, FISH_CBC_MODE); + g_assert_nonnull(de); + g_assert_cmpuint(g_strcmp0(de, message), == , 0); + g_free(de); + + /* Free */ + g_free(b64); + } + } +} + +/** + * Check the calculation of final length from an encoded string in Base64 + */ +void __base64_len() { + char *b64 = NULL; + int i, message_len = 0; + char message[1000]; + + for (i = 0; i < 10; ++i) { + + for (message_len = 1; message_len < 1000; ++message_len) { + random_string(message, message_len); + b64 = g_base64_encode((const unsigned char *) message, message_len); + g_assert_nonnull(b64); + g_assert_cmpuint(strlen(b64), == , base64_len(message_len)); + g_free(b64); + } + } +} + +/** + * Check the calculation of final length from an encoded string in BlowcryptBase64 + */ +void __base64_fish_len() { + char *b64 = NULL; + int i, message_len = 0; + char message[1000]; + + for (i = 0; i < 10; ++i) { + + for (message_len = 1; message_len < 1000; ++message_len) { + random_string(message, message_len); + b64 = fish_base64_encode(message, message_len); + g_assert_nonnull(b64); + g_assert_cmpuint(strlen(b64), == , base64_fish_len(message_len)); + g_free(b64); + } + } +} + + +/** + * Check the calculation of final length from an encrypted string in ECB mode + */ +void __base64_ecb_len() { + char *b64 = NULL; + int key_len, message_len = 0; + char key[57]; + char message[1000]; + + /* Generate key 32–448 bits (Yes, I start with 8 bits) */ + for (key_len = 1; key_len < 57; ++key_len) { + + random_string(key, key_len); + + for (message_len = 1; message_len < 1000; ++message_len) { + random_string(message, message_len); + b64 = fish_encrypt(key, key_len, message, message_len, FISH_ECB_MODE); + g_assert_nonnull(b64); + g_assert_cmpuint(strlen(b64), == , ecb_len(message_len)); + g_free(b64); + } + } +} + +/** + * Check the calculation of final length from an encrypted string in CBC mode + */ +void __base64_cbc_len() { + char *b64 = NULL; + int key_len, message_len = 0; + char key[57]; + char message[1000]; + + /* Generate key 32–448 bits (Yes, I start with 8 bits) */ + for (key_len = 1; key_len < 57; ++key_len) { + + random_string(key, key_len); + + for (message_len = 1; message_len < 1000; ++message_len) { + random_string(message, message_len); + b64 = fish_encrypt(key, key_len, message, message_len, FISH_CBC_MODE); + g_assert_nonnull(b64); + g_assert_cmpuint(strlen(b64), == , cbc_len(message_len)); + g_free(b64); + } + } +} + +int main(int argc, char *argv[]) { + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/fishlim/__ecb", __ecb); + g_test_add_func("/fishlim/__cbc", __ecb); + g_test_add_func("/fishlim/__base64_len", __base64_len); + g_test_add_func("/fishlim/__base64_fish_len", __base64_fish_len); + g_test_add_func("/fishlim/__base64_ecb_len", __base64_ecb_len); + g_test_add_func("/fishlim/__base64_cbc_len", __base64_cbc_len); + + return g_test_run(); +} + +#endif //PLUGIN_HEXCHAT_FISHLIM_TEST_H \ No newline at end of file -- cgit 1.4.1 From 4758d3705d652b8cda006e8902d8e8c6921cdee1 Mon Sep 17 00:00:00 2001 From: BakasuraRCE Date: Mon, 20 Jul 2020 17:58:15 -0500 Subject: fishlim: Fix result --- plugins/fishlim/tests/fake/keystore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins/fishlim/tests') diff --git a/plugins/fishlim/tests/fake/keystore.c b/plugins/fishlim/tests/fake/keystore.c index 855b3451..854f38dc 100644 --- a/plugins/fishlim/tests/fake/keystore.c +++ b/plugins/fishlim/tests/fake/keystore.c @@ -36,12 +36,12 @@ char *keystore_get_key(const char *nick, enum fish_mode *mode) { * Sets a key in the key store file. */ gboolean keystore_store_key(const char *nick, const char *key, enum fish_mode mode) { - return NULL; + return TRUE; } /** * Deletes a nick from the key store. */ gboolean keystore_delete_nick(const char *nick) { - return NULL; + return TRUE; } -- cgit 1.4.1 From c7844c775ac2f2ab71722227edb941b4086baf91 Mon Sep 17 00:00:00 2001 From: BakasuraRCE Date: Tue, 21 Jul 2020 18:35:00 -0500 Subject: fishlim: Remove needless functions for tests --- plugins/fishlim/tests/old_version/fish.c | 41 +------------------------------- plugins/fishlim/tests/old_version/fish.h | 2 -- 2 files changed, 1 insertion(+), 42 deletions(-) (limited to 'plugins/fishlim/tests') diff --git a/plugins/fishlim/tests/old_version/fish.c b/plugins/fishlim/tests/old_version/fish.c index ff0b8fea..99601398 100644 --- a/plugins/fishlim/tests/old_version/fish.c +++ b/plugins/fishlim/tests/old_version/fish.c @@ -152,43 +152,4 @@ char *__old_fish_decrypt(const char *key, size_t keylen, const char *data) { decrypt_end: *end = '\0'; return decrypted; -} - -/** - * Encrypts a message (see __old_fish_decrypt). The key is searched for in the - * key store. - */ -char *__old_fish_encrypt_for_nick(const char *nick, const char *data) { - char *key; - char *encrypted; - - /* Look for key */ - /*key = keystore_get_key(nick);*/ - if (!key) return NULL; - - /* Encrypt */ - encrypted = __old_fish_encrypt(key, strlen(key), data); - - g_free(key); - return encrypted; -} - -/** - * Decrypts a message (see __old_fish_decrypt). The key is searched for in the - * key store. - */ -char *__old_fish_decrypt_from_nick(const char *nick, const char *data) { - char *key; - char *decrypted; - /* Look for key */ - /*key = keystore_get_key(nick);*/ - if (!key) return NULL; - - /* Decrypt */ - decrypted = __old_fish_decrypt(key, strlen(key), data); - - g_free(key); - return decrypted; -} - - +} \ No newline at end of file diff --git a/plugins/fishlim/tests/old_version/fish.h b/plugins/fishlim/tests/old_version/fish.h index b4c66572..7037782b 100644 --- a/plugins/fishlim/tests/old_version/fish.h +++ b/plugins/fishlim/tests/old_version/fish.h @@ -31,8 +31,6 @@ char *__old_fish_encrypt(const char *key, size_t keylen, const char *message); char *__old_fish_decrypt(const char *key, size_t keylen, const char *data); -char *__old_fish_encrypt_for_nick(const char *nick, const char *data); -char *__old_fish_decrypt_from_nick(const char *nick, const char *data); #endif -- cgit 1.4.1 From df818ad7d99cecaec58b293724cc975801e19079 Mon Sep 17 00:00:00 2001 From: BakasuraRCE Date: Tue, 21 Jul 2020 18:35:57 -0500 Subject: fishlim: Remove compiler warnings --- plugins/fishlim/tests/tests.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'plugins/fishlim/tests') diff --git a/plugins/fishlim/tests/tests.c b/plugins/fishlim/tests/tests.c index e34532bf..b75ec653 100644 --- a/plugins/fishlim/tests/tests.c +++ b/plugins/fishlim/tests/tests.c @@ -56,7 +56,7 @@ void random_string(char *out, size_t len) { /** * Check encrypt and decrypt in ECB mode and compare with old implementation */ -void __ecb() { +void __ecb(void) { char *bo64 = NULL, *b64 = NULL; char *deo = NULL, *de = NULL; int key_len, message_len = 0; @@ -110,7 +110,7 @@ void __ecb() { /** * Check encrypt and decrypt in CBC mode */ -void __cbc() { +void __cbc(void) { char *b64 = NULL; char *de = NULL; int key_len, message_len = 0; @@ -145,7 +145,7 @@ void __cbc() { /** * Check the calculation of final length from an encoded string in Base64 */ -void __base64_len() { +void __base64_len(void) { char *b64 = NULL; int i, message_len = 0; char message[1000]; @@ -165,7 +165,7 @@ void __base64_len() { /** * Check the calculation of final length from an encoded string in BlowcryptBase64 */ -void __base64_fish_len() { +void __base64_fish_len(void) { char *b64 = NULL; int i, message_len = 0; char message[1000]; @@ -186,7 +186,7 @@ void __base64_fish_len() { /** * Check the calculation of final length from an encrypted string in ECB mode */ -void __base64_ecb_len() { +void __base64_ecb_len(void) { char *b64 = NULL; int key_len, message_len = 0; char key[57]; @@ -210,7 +210,7 @@ void __base64_ecb_len() { /** * Check the calculation of final length from an encrypted string in CBC mode */ -void __base64_cbc_len() { +void __base64_cbc_len(void) { char *b64 = NULL; int key_len, message_len = 0; char key[57]; -- cgit 1.4.1 From bd3f3fa5f70c624926d06884b328de59730fc93c Mon Sep 17 00:00:00 2001 From: BakasuraRCE Date: Tue, 21 Jul 2020 19:13:02 -0500 Subject: fishlim: Remove needless header --- plugins/fishlim/tests/tests.c | 1 - 1 file changed, 1 deletion(-) (limited to 'plugins/fishlim/tests') diff --git a/plugins/fishlim/tests/tests.c b/plugins/fishlim/tests/tests.c index b75ec653..45dfd41c 100644 --- a/plugins/fishlim/tests/tests.c +++ b/plugins/fishlim/tests/tests.c @@ -26,7 +26,6 @@ #define PLUGIN_HEXCHAT_FISHLIM_TEST_H // Libs -#include #include // Project Libs #include "../fish.h" -- cgit 1.4.1 From 078af20e8b3bff93dd42972e4ef01702e7e1fe2b Mon Sep 17 00:00:00 2001 From: BakasuraRCE Date: Tue, 21 Jul 2020 19:16:50 -0500 Subject: fishlim: Implement correct handling of long and UTF-8 messages --- plugins/fishlim/fish.c | 78 ++++++++-- plugins/fishlim/fish.h | 5 +- plugins/fishlim/fishlim.vcxproj | 2 + plugins/fishlim/fishlim.vcxproj.filters | 6 + plugins/fishlim/meson.build | 1 + plugins/fishlim/plugin_hexchat.c | 254 ++++++++++++++++++++++++++------ plugins/fishlim/tests/tests.c | 50 +++++++ plugins/fishlim/utils.c | 79 ++++++++++ plugins/fishlim/utils.h | 4 + 9 files changed, 413 insertions(+), 66 deletions(-) (limited to 'plugins/fishlim/tests') diff --git a/plugins/fishlim/fish.c b/plugins/fishlim/fish.c index 88f7caec..c2c2b9da 100644 --- a/plugins/fishlim/fish.c +++ b/plugins/fishlim/fish.c @@ -1,7 +1,7 @@ /* Copyright (c) 2010 Samuel Lidén Borell - Copyright (c) 2019 + Copyright (c) 2019-2020 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -37,8 +37,11 @@ #include "keystore.h" #include "fish.h" +#include "utils.h" #define IB 64 +/* rfc 2812; 512 - CR-LF = 510 */ +static const int MAX_COMMAND_LENGTH = 510; static const char fish_base64[] = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; static const signed char fish_unbase64[256] = { @@ -401,13 +404,40 @@ char *fish_decrypt_str(const char *key, size_t keylen, const char *data, enum fi } /** - * Encrypts a message (see fish_decrypt). The key is searched for in the - * key store. + * Determine if a nick have a key + * + * @param [in] nick Nickname + * @return TRUE if have a key or FALSE if not + */ +gboolean fish_nick_has_key(const char *nick) { + gboolean has_key = FALSE; + char *key; + enum fish_mode mode; + + key = keystore_get_key(nick, &mode); + if (key) { + has_key = TRUE; + g_free(key); + }; + + return has_key; +} + +/** + * Encrypts a message (see fish_encrypt). The key is searched for in the key store + * + * @param [in] nick Nickname + * @param [in] data Plaintext to encrypt + * @param [out] omode Mode of encryption + * @param [in] command_len Length of command to use without the message part + * @return A list of encoded strings with the message encrypted or NULL if any error occurred */ -char *fish_encrypt_for_nick(const char *nick, const char *data, enum fish_mode *omode) { +GSList *fish_encrypt_for_nick(const char *nick, const char *data, enum fish_mode *omode, size_t command_len) { char *key; - char *encrypted, *encrypted_cbc = NULL; + GSList *encrypted_list = NULL; + char *encrypted = NULL; enum fish_mode mode; + int max_len, max_chunks_len, chunks_len; /* Look for key */ key = keystore_get_key(nick, &mode); @@ -415,24 +445,40 @@ char *fish_encrypt_for_nick(const char *nick, const char *data, enum fish_mode * *omode = mode; - /* Encrypt */ - encrypted = fish_encrypt(key, strlen(key), data, strlen(data), mode); + /* Calculate max length of each line */ + max_len = MAX_COMMAND_LENGTH - command_len; + /* Add '*' */ + if (mode == FISH_CBC_MODE) max_len--; - g_free(key); + max_chunks_len = max_text_command_len(max_len, mode); - if (encrypted == NULL || mode == FISH_ECB_MODE) - return encrypted; + const char *data_chunk = data; - /* Add '*' for CBC */ - encrypted_cbc = g_strdup_printf("*%s",encrypted); - g_free(encrypted); + while(foreach_utf8_data_chunks(data_chunk, max_chunks_len, &chunks_len)) { + encrypted = fish_encrypt(key, strlen(key), data_chunk, chunks_len, mode); - return encrypted_cbc; + if (mode == FISH_CBC_MODE) { + /* Add '*' for CBC */ + encrypted_list = g_slist_append(encrypted_list, g_strdup_printf("*%s", encrypted)); + g_free(encrypted); + } else { + encrypted_list = g_slist_append(encrypted_list, encrypted); + } + + /* Next chunk */ + data_chunk += chunks_len; + } + + return encrypted_list; } /** - * Decrypts a message (see fish_decrypt). The key is searched for in the - * key store. + * Decrypts a message (see fish_decrypt). The key is searched for in the key store + * + * @param [in] nick Nickname + * @param [in] data Plaintext to encrypt + * @param [out] omode Mode of encryption + * @return Plaintext message or NULL if any error occurred */ char *fish_decrypt_from_nick(const char *nick, const char *data, enum fish_mode *omode) { char *key; diff --git a/plugins/fishlim/fish.h b/plugins/fishlim/fish.h index daf17acf..6a2e911c 100644 --- a/plugins/fishlim/fish.h +++ b/plugins/fishlim/fish.h @@ -1,7 +1,7 @@ /* Copyright (c) 2010 Samuel Lidén Borell - Copyright (c) 2019 + Copyright (c) 2019-2020 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -40,7 +40,8 @@ char *fish_base64_decode(const char *message, size_t *final_len); char *fish_encrypt(const char *key, size_t keylen, const char *message, size_t message_len, enum fish_mode mode); char *fish_decrypt(const char *key, size_t keylen, const char *data, enum fish_mode mode, size_t *final_len); char *fish_decrypt_str(const char *key, size_t keylen, const char *data, enum fish_mode mode); -char *fish_encrypt_for_nick(const char *nick, const char *data, enum fish_mode *omode); +gboolean fish_nick_has_key(const char *nick); +GSList *fish_encrypt_for_nick(const char *nick, const char *data, enum fish_mode *omode, size_t command_len); char *fish_decrypt_from_nick(const char *nick, const char *data, enum fish_mode *omode); #endif diff --git a/plugins/fishlim/fishlim.vcxproj b/plugins/fishlim/fishlim.vcxproj index 43ae1df9..157c7928 100644 --- a/plugins/fishlim/fishlim.vcxproj +++ b/plugins/fishlim/fishlim.vcxproj @@ -55,6 +55,7 @@ + @@ -62,6 +63,7 @@ + diff --git a/plugins/fishlim/fishlim.vcxproj.filters b/plugins/fishlim/fishlim.vcxproj.filters index 09649ec9..ee4e855a 100644 --- a/plugins/fishlim/fishlim.vcxproj.filters +++ b/plugins/fishlim/fishlim.vcxproj.filters @@ -29,6 +29,9 @@ Header Files + + Header Files + Header Files @@ -46,6 +49,9 @@ Source Files + + Source Files + Source Files diff --git a/plugins/fishlim/meson.build b/plugins/fishlim/meson.build index 06cf5f9a..65ccc9ea 100644 --- a/plugins/fishlim/meson.build +++ b/plugins/fishlim/meson.build @@ -8,6 +8,7 @@ subdir('tests') fishlim_sources = [ 'dh1080.c', 'fish.c', + 'utils.c', 'irc.c', 'keystore.c', 'plugin_hexchat.c' diff --git a/plugins/fishlim/plugin_hexchat.c b/plugins/fishlim/plugin_hexchat.c index ddb692da..83286e28 100644 --- a/plugins/fishlim/plugin_hexchat.c +++ b/plugins/fishlim/plugin_hexchat.c @@ -2,7 +2,7 @@ Copyright (c) 2010-2011 Samuel Lidén Borell Copyright (c) 2015 - Copyright (c) 2019 + Copyright (c) 2019-2020 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,6 @@ #include #include #include - #include "hexchat-plugin.h" #include "fish.h" @@ -108,13 +107,14 @@ static hexchat_context *find_context_on_network (const char *name) { } /** - * Retrive the prefix character for own nick in current context - * @return @ or + or NULL + * Retrive the field for own user in current context + * @return the field value */ -char *get_my_own_prefix(void) { +char *get_my_info(const char *field, gboolean find_in_other_context) { char *result = NULL; const char *own_nick; hexchat_list *list; + hexchat_context *ctx_current, *ctx_channel; /* Display message */ own_nick = hexchat_get_info(ph, "nick"); @@ -122,12 +122,38 @@ char *get_my_own_prefix(void) { if (!own_nick) return NULL; - /* Get prefix for own nick if any */ - list = hexchat_list_get (ph, "users"); + /* Get field for own nick if any */ + list = hexchat_list_get(ph, "users"); if (list) { while (hexchat_list_next(ph, list)) { if (irc_nick_cmp(own_nick, hexchat_list_str(ph, list, "nick")) == 0) - result = g_strdup(hexchat_list_str(ph, list, "prefix")); + result = g_strdup(hexchat_list_str(ph, list, field)); + } + hexchat_list_free(ph, list); + } + + if (result) { + return result; + } + + /* Try to get from a channel (we are outside a channel) */ + if (!find_in_other_context) { + return NULL; + } + + list = hexchat_list_get(ph, "channels"); + if (list) { + ctx_current = hexchat_get_context(ph); + while (hexchat_list_next(ph, list)) { + ctx_channel = (hexchat_context *) hexchat_list_str(ph, list, "context"); + + hexchat_set_context(ph, ctx_channel); + result = get_my_info(field, FALSE); + hexchat_set_context(ph, ctx_current); + + if (result) { + break; + } } hexchat_list_free(ph, list); } @@ -135,6 +161,45 @@ char *get_my_own_prefix(void) { return result; } +/** + * Retrive the prefix character for own nick in current context + * @return @ or + or NULL + */ +char *get_my_own_prefix(void) { + return get_my_info("prefix", FALSE); +} + +/** + * Retrive the mask for own nick in current context + * @return Host name in the form: user@host (or NULL if not known) + */ +char *get_my_own_host(void) { + return get_my_info("host", TRUE); +} + +/** + * Calculate the length of prefix for current user in current context + * + * @return Length of prefix + */ +int get_prefix_length(void) { + char *own_host; + int prefix_len = 0; + + /* ':! ' + 'nick' + 'ident@host', e.g. ':user!~name@mynet.com ' */ + prefix_len = 3 + strlen(hexchat_get_info(ph, "nick")); + own_host = get_my_own_host(); + if (own_host) { + prefix_len += strlen(own_host); + } else { + /* https://stackoverflow.com/questions/8724954/what-is-the-maximum-number-of-characters-for-a-host-name-in-unix */ + prefix_len += 64; + } + g_free(own_host); + + return prefix_len; +} + /** * Try to decrypt the first occurrence of fish message * @@ -228,11 +293,23 @@ static int handle_outgoing(char *word[], char *word_eol[], void *userdata) { char *prefix; enum fish_mode mode; char *message; - /* Encrypt the message if possible */ + GString *command; + GSList *encrypted_list, *encrypted_item; + const char *channel = hexchat_get_info(ph, "channel"); - char *encrypted = fish_encrypt_for_nick(channel, word_eol[1], &mode); - if (!encrypted) return HEXCHAT_EAT_NONE; - + + /* Check if we can encrypt */ + if (!fish_nick_has_key(channel)) return HEXCHAT_EAT_NONE; + + command = g_string_new(""); + g_string_printf(command, "PRIVMSG %s :+OK ", channel); + + encrypted_list = fish_encrypt_for_nick(channel, word_eol[1], &mode, get_prefix_length() + command->len); + if (!encrypted_list) { + g_string_free(command, TRUE); + return HEXCHAT_EAT_NONE; + } + /* Get prefix for own nick if any */ prefix = get_my_own_prefix(); @@ -241,13 +318,21 @@ static int handle_outgoing(char *word[], char *word_eol[], void *userdata) { /* Display message */ hexchat_emit_print(ph, "Your Message", hexchat_get_info(ph, "nick"), message, prefix, NULL); - g_free(prefix); g_free(message); - - /* Send message */ - hexchat_commandf(ph, "PRIVMSG %s :+OK %s", channel, encrypted); - - g_free(encrypted); + + /* Send encrypted messages */ + encrypted_item = encrypted_list; + while (encrypted_item) + { + hexchat_commandf(ph, "%s%s", command->str, (char *)encrypted_item->data); + + encrypted_item = encrypted_item->next; + } + + g_free(prefix); + g_string_free(command, TRUE); + g_slist_free_full(encrypted_list, g_free); + return HEXCHAT_EAT_HEXCHAT; } @@ -498,8 +583,9 @@ static int handle_keyx(char *word[], char *word_eol[], void *userdata) { static int handle_crypt_topic(char *word[], char *word_eol[], void *userdata) { const char *target; const char *topic = word_eol[2]; - char *buf; enum fish_mode mode; + GString *command; + GSList *encrypted_list; if (!*topic) { hexchat_print(ph, usage_topic); @@ -512,44 +598,77 @@ static int handle_crypt_topic(char *word[], char *word_eol[], void *userdata) { } target = hexchat_get_info(ph, "channel"); - buf = fish_encrypt_for_nick(target, topic, &mode); - if (buf == NULL) { + + /* Check if we can encrypt */ + if (!fish_nick_has_key(target)) { hexchat_printf(ph, "/topic+ error, no key found for %s", target); return HEXCHAT_EAT_ALL; } - hexchat_commandf(ph, "TOPIC %s +OK %s", target, buf); - g_free(buf); - return HEXCHAT_EAT_ALL; + command = g_string_new(""); + g_string_printf(command, "TOPIC %s +OK ", target); + + encrypted_list = fish_encrypt_for_nick(target, topic, &mode, get_prefix_length() + command->len); + if (!encrypted_list) { + g_string_free(command, TRUE); + hexchat_printf(ph, "/topic+ error, can't encrypt %s", target); + return HEXCHAT_EAT_ALL; } + hexchat_commandf(ph, "%s%s", command->str, (char *) encrypted_list->data); + + g_string_free(command, TRUE); + g_slist_free_full(encrypted_list, g_free); + + return HEXCHAT_EAT_ALL; +} + /** * Command handler for /notice+ */ -static int handle_crypt_notice(char *word[], char *word_eol[], void *userdata) -{ +static int handle_crypt_notice(char *word[], char *word_eol[], void *userdata) { const char *target = word[2]; const char *notice = word_eol[3]; char *notice_flag; - char *buf; enum fish_mode mode; + GString *command; + GSList *encrypted_list, *encrypted_item; if (!*target || !*notice) { hexchat_print(ph, usage_notice); return HEXCHAT_EAT_ALL; } - buf = fish_encrypt_for_nick(target, notice, &mode); - if (buf == NULL) { + /* Check if we can encrypt */ + if (!fish_nick_has_key(target)) { hexchat_printf(ph, "/notice+ error, no key found for %s.", target); return HEXCHAT_EAT_ALL; } - hexchat_commandf(ph, "quote NOTICE %s :+OK %s", target, buf); + command = g_string_new(""); + g_string_printf(command, "quote NOTICE %s :+OK ", target); + + encrypted_list = fish_encrypt_for_nick(target, notice, &mode, get_prefix_length() + command->len); + if (!encrypted_list) { + g_string_free(command, TRUE); + hexchat_printf(ph, "/notice+ error, can't encrypt %s", target); + return HEXCHAT_EAT_ALL; + } + notice_flag = g_strconcat("[", fish_modes[mode], "] ", notice, NULL); hexchat_emit_print(ph, "Notice Send", target, notice_flag); + + /* Send encrypted messages */ + encrypted_item = encrypted_list; + while (encrypted_item) { + hexchat_commandf(ph, "%s%s", command->str, (char *) encrypted_item->data); + + encrypted_item = encrypted_item->next; + } + g_free(notice_flag); - g_free(buf); + g_string_free(command, TRUE); + g_slist_free_full(encrypted_list, g_free); return HEXCHAT_EAT_ALL; } @@ -563,21 +682,41 @@ static int handle_crypt_msg(char *word[], char *word_eol[], void *userdata) { char *message_flag; char *prefix; hexchat_context *query_ctx; - char *buf; enum fish_mode mode; + GString *command; + GSList *encrypted_list, *encrypted_item; if (!*target || !*message) { hexchat_print(ph, usage_msg); return HEXCHAT_EAT_ALL; } - buf = fish_encrypt_for_nick(target, message, &mode); - if (buf == NULL) { + /* Check if we can encrypt */ + if (!fish_nick_has_key(target)) { hexchat_printf(ph, "/msg+ error, no key found for %s", target); return HEXCHAT_EAT_ALL; } - hexchat_commandf(ph, "PRIVMSG %s :+OK %s", target, buf); + command = g_string_new(""); + g_string_printf(command, "PRIVMSG %s :+OK ", target); + + encrypted_list = fish_encrypt_for_nick(target, message, &mode, get_prefix_length() + command->len); + if (!encrypted_list) { + g_string_free(command, TRUE); + hexchat_printf(ph, "/msg+ error, can't encrypt %s", target); + return HEXCHAT_EAT_ALL; + } + + /* Send encrypted messages */ + encrypted_item = encrypted_list; + while (encrypted_item) { + hexchat_commandf(ph, "%s%s", command->str, (char *) encrypted_item->data); + + encrypted_item = encrypted_item->next; + } + + g_string_free(command, TRUE); + g_slist_free_full(encrypted_list, g_free); query_ctx = find_context_on_network(target); if (query_ctx) { @@ -587,33 +726,52 @@ static int handle_crypt_msg(char *word[], char *word_eol[], void *userdata) { /* Add encrypted flag */ message_flag = g_strconcat("[", fish_modes[mode], "] ", message, NULL); - hexchat_emit_print(ph, "Your Message", hexchat_get_info(ph, "nick"), - message_flag, prefix, NULL); + hexchat_emit_print(ph, "Your Message", hexchat_get_info(ph, "nick"), message_flag, prefix, NULL); g_free(prefix); g_free(message_flag); } else { hexchat_emit_print(ph, "Message Send", target, message); } - g_free(buf); return HEXCHAT_EAT_ALL; } static int handle_crypt_me(char *word[], char *word_eol[], void *userdata) { - const char *channel = hexchat_get_info(ph, "channel"); - char *buf; - enum fish_mode mode; + const char *channel = hexchat_get_info(ph, "channel"); + enum fish_mode mode; + GString *command; + GSList *encrypted_list, *encrypted_item; - buf = fish_encrypt_for_nick(channel, word_eol[2], &mode); - if (!buf) + /* Check if we can encrypt */ + if (!fish_nick_has_key(channel)) { return HEXCHAT_EAT_NONE; + } + + command = g_string_new(""); + g_string_printf(command, "PRIVMSG %s :\001ACTION +OK ", channel); + + /* 2 = ' \001' */ + encrypted_list = fish_encrypt_for_nick(channel, word_eol[2], &mode, get_prefix_length() + command->len + 2); + if (!encrypted_list) { + g_string_free(command, TRUE); + hexchat_printf(ph, "/me error, can't encrypt %s", channel); + return HEXCHAT_EAT_ALL; + } - hexchat_commandf(ph, "PRIVMSG %s :\001ACTION +OK %s \001", channel, buf); - hexchat_emit_print(ph, "Your Action", hexchat_get_info(ph, "nick"), - word_eol[2], NULL); + hexchat_emit_print(ph, "Your Action", hexchat_get_info(ph, "nick"), word_eol[2], NULL); - g_free(buf); - return HEXCHAT_EAT_ALL; + /* Send encrypted messages */ + encrypted_item = encrypted_list; + while (encrypted_item) { + hexchat_commandf(ph, "%s%s \001", command->str, (char *) encrypted_item->data); + + encrypted_item = encrypted_item->next; + } + + g_string_free(command, TRUE); + g_slist_free_full(encrypted_list, g_free); + + return HEXCHAT_EAT_ALL; } /** diff --git a/plugins/fishlim/tests/tests.c b/plugins/fishlim/tests/tests.c index 45dfd41c..bb841c5e 100644 --- a/plugins/fishlim/tests/tests.c +++ b/plugins/fishlim/tests/tests.c @@ -230,6 +230,54 @@ void __base64_cbc_len(void) { } } +/** + * Check the calculation of length limit for a plaintext in each encryption mode + */ +void __max_text_command_len(void) { + int max_encoded_len, plaintext_len; + enum fish_mode mode; + + for (max_encoded_len = 0; max_encoded_len < 10000; ++max_encoded_len) { + for (mode = FISH_ECB_MODE; mode <= FISH_CBC_MODE; ++mode) { + plaintext_len = max_text_command_len(max_encoded_len, mode); + g_assert_cmpuint(encoded_len(plaintext_len, mode), <= , max_encoded_len); + } + } +} + +/** + * Check the calculation of length limit for a plaintext in each encryption mode + */ +void __foreach_utf8_data_chunks(void) { + GRand *rand = NULL; + GString *chunks = NULL; + int tests, max_chunks_len, chunks_len; + char ascii_message[1001]; + char *data_chunk = NULL; + + rand = g_rand_new(); + + for (tests = 0; tests < 1000; ++tests) { + + max_chunks_len = g_rand_int_range(rand, 2, 301); + random_string(ascii_message, 1000); + + data_chunk = ascii_message; + + chunks = g_string_new(NULL); + + while (foreach_utf8_data_chunks(data_chunk, max_chunks_len, &chunks_len)) { + g_string_append(chunks, g_strndup(data_chunk, chunks_len)); + /* Next chunk */ + data_chunk += chunks_len; + } + /* Check data loss */ + g_assert_cmpstr(chunks->str, == , ascii_message); + g_string_free(chunks, TRUE); + } +} + + int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); @@ -240,6 +288,8 @@ int main(int argc, char *argv[]) { g_test_add_func("/fishlim/__base64_fish_len", __base64_fish_len); g_test_add_func("/fishlim/__base64_ecb_len", __base64_ecb_len); g_test_add_func("/fishlim/__base64_cbc_len", __base64_cbc_len); + g_test_add_func("/fishlim/__max_text_command_len", __max_text_command_len); + g_test_add_func("/fishlim/__foreach_utf8_data_chunks", __foreach_utf8_data_chunks); return g_test_run(); } diff --git a/plugins/fishlim/utils.c b/plugins/fishlim/utils.c index 5af87404..4052995a 100644 --- a/plugins/fishlim/utils.c +++ b/plugins/fishlim/utils.c @@ -23,6 +23,7 @@ */ #include "utils.h" +#include "fish.h" /** * Calculate the length of Base64-encoded string @@ -67,4 +68,82 @@ unsigned long cbc_len(size_t plaintext_len) { */ unsigned long ecb_len(size_t plaintext_len) { return base64_fish_len(plaintext_len); +} + +/** + * Calculate the length of encrypted string in 'mode' mode + * + * @param plaintext_len Length of plaintext + * @param mode Encryption mode + * @return Size of encoded string + */ +unsigned long encoded_len(size_t plaintext_len, enum fish_mode mode) { + switch (mode) { + + case FISH_CBC_MODE: + return cbc_len(plaintext_len); + break; + + case FISH_ECB_MODE: + return ecb_len(plaintext_len); + } + + return 0; +} + +/** + * Determine the maximum length of plaintext for a 'max_len' limit taking care the overload of encryption + * + * @param max_len Limit for plaintext + * @param mode Encryption mode + * @return Maximum allowed plaintext length + */ +int max_text_command_len(size_t max_len, enum fish_mode mode) { + int len; + + for (len = max_len; encoded_len(len, mode) > max_len; --len); + return len; +} + +/** + * Iterate over 'data' in chunks of 'max_chunk_len' taking care the UTF-8 characters + * + * @param data Data to iterate + * @param max_chunk_len Size of biggest chunk + * @param [out] chunk_len Current chunk length + * @return Pointer to current chunk position or NULL if not have more chunks + */ +const char *foreach_utf8_data_chunks(const char *data, int max_chunk_len, int *chunk_len) { + int data_len, last_chunk_len = 0; + + if (!*data) { + return NULL; + } + + /* Last chunk of data */ + data_len = strlen(data); + if (data_len <= max_chunk_len) { + *chunk_len = data_len; + return data; + } + + *chunk_len = 0; + const char *utf8_character = data; + + /* Not valid UTF-8, but maybe valid text, just split into max length */ + if (!g_utf8_validate(data, -1, NULL)) { + *chunk_len = max_chunk_len; + return utf8_character; + } + + while (*utf8_character && *chunk_len <= max_chunk_len) { + last_chunk_len = *chunk_len; + *chunk_len = (g_utf8_next_char(utf8_character) - data) * sizeof(*utf8_character); + utf8_character = g_utf8_next_char(utf8_character); + } + + /* We need the previous length before overflow the limit */ + *chunk_len = last_chunk_len; + + return utf8_character; } \ No newline at end of file diff --git a/plugins/fishlim/utils.h b/plugins/fishlim/utils.h index 9e2b3355..623c67a4 100644 --- a/plugins/fishlim/utils.h +++ b/plugins/fishlim/utils.h @@ -26,10 +26,14 @@ #define PLUGIN_HEXCHAT_FISHLIM_UTILS_H #include +#include "fish.h" unsigned long base64_len(size_t plaintext_len); unsigned long base64_fish_len(size_t plaintext_len); unsigned long cbc_len(size_t plaintext_len); unsigned long ecb_len(size_t plaintext_len); +unsigned long encoded_len(size_t plaintext_len, enum fish_mode mode); +int max_text_command_len(size_t max_len, enum fish_mode mode); +const char *foreach_utf8_data_chunks(const char *data, int max_chunk_len, int *chunk_len); #endif \ No newline at end of file -- cgit 1.4.1