From 4acdfffc657b3c7786cfda206ec4fe978d702c41 Mon Sep 17 00:00:00 2001 From: Heiko Reese Date: Tue, 17 Aug 2021 20:57:58 +0200 Subject: [PATCH] =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/genkey.c | 34 ++---- src/libexim-encrypt-dlfunc.c | 181 +++++++++++++++++++++++++++++--- src/libexim-kitencrypt-dlfunc.c | 61 ----------- src/simple_exim_test.sh | 5 + 4 files changed, 178 insertions(+), 103 deletions(-) delete mode 100644 src/libexim-kitencrypt-dlfunc.c diff --git a/src/genkey.c b/src/genkey.c index 75dab8e..576b833 100644 --- a/src/genkey.c +++ b/src/genkey.c @@ -32,11 +32,15 @@ void dump_key_as_exim_config(FILE * f, const char *name, unsigned char *key, unsigned int keylen) { - fprintf(f, "%s = \"", name); - for (int i = 0; i < keylen; i++) { - fprintf(f, "\\x%02x", key[i]); - } - fprintf(f, "\"\n"); + // encode with base64 + unsigned int b64len = sodium_base64_ENCODED_LEN(keylen, sodium_base64_VARIANT_ORIGINAL); + unsigned char *b64string = malloc(b64len); + sodium_bin2base64((char *const) b64string, b64len, + key, keylen, + sodium_base64_VARIANT_ORIGINAL); + fprintf(f, "%s = \"%s\"\n", name, b64string); + + free(b64string); } void @@ -52,21 +56,6 @@ write_key_files(const char *filebase, const char *varname, sprintf(exim_filename, "%s_exim.conf", filebase); sprintf(raw_filename, "%s.raw", filebase); - /* - // open header file - f = fopen(header_filename, "w+"); - if (f == NULL) { - fprintf(stderr, "Unable to open %s for writing", - header_filename); - exit(129); - } - // write key as C header - dump_key_as_c_code(f, varname, key, keylen); - - // close header file - fclose(f); - */ - // open exim config snippet file f = fopen(exim_filename, "w+"); if (f == NULL) { @@ -100,11 +89,6 @@ void create_cryptobox_keys(const char *filebase, const char *varname) unsigned char recipient_pk[crypto_box_PUBLICKEYBYTES]; unsigned char recipient_sk[crypto_box_SECRETKEYBYTES]; - while (key_contains_zero(recipient_pk, crypto_box_PUBLICKEYBYTES) & - key_contains_zero(recipient_sk, crypto_box_SECRETKEYBYTES)) { - crypto_box_keypair(recipient_pk, recipient_sk); - } - char pk_filename[4096]; char pk_varname[4096]; char sk_filename[4096]; diff --git a/src/libexim-encrypt-dlfunc.c b/src/libexim-encrypt-dlfunc.c index 03cd76d..1ad607f 100644 --- a/src/libexim-encrypt-dlfunc.c +++ b/src/libexim-encrypt-dlfunc.c @@ -6,9 +6,23 @@ /* Exim4 dlfunc API header */ #include +int b64decode(unsigned char * const input, const size_t input_len, unsigned char * output) { + unsigned int output_len = input_len / 4 * 3; + output = store_get(output_len); + sodium_memzero(output, output_len); + int b64err = sodium_base642bin(output, output_len, (const char *) input, input_len, NULL, NULL, NULL, sodium_base64_VARIANT_ORIGINAL); + if (b64err != 0) { + return -1; + } + return output_len; +} + /* - * Encrypt the second argument with password string from the first argument - * using crypto_secretbox_easy(). + * Encrypt message using crypto_secretbox_easy(). + * + * Arguments: + * 1. password + * 2. cleartext message */ int sodium_crypto_secretbox_encrypt_password(uschar **yield, int argc, uschar *argv[]) { // ensure libsodium is initialized @@ -48,11 +62,14 @@ int sodium_crypto_secretbox_encrypt_password(uschar **yield, int argc, uschar *a // encrypt message unsigned char nonce[crypto_secretbox_NONCEBYTES]; randombytes_buf(nonce, sizeof nonce); - crypto_secretbox_easy(ciphertext, message, messagelen, nonce, keybytes); + if (crypto_secretbox_easy(ciphertext, message, messagelen, nonce, keybytes) != 0) { + *yield = string_copy((unsigned char *) "Encryption error after crypto_secretbox_easy()"); + return ERROR; + } // combine nonce and ciphertext size_t combined_message_len = crypto_secretbox_NONCEBYTES + cipherlen; - unsigned char * combined_message = store_get(combined_message_len); + unsigned char *combined_message = store_get(combined_message_len); memcpy(combined_message, nonce, crypto_secretbox_NONCEBYTES); memcpy(&combined_message[crypto_secretbox_NONCEBYTES], ciphertext, cipherlen); @@ -60,7 +77,7 @@ int sodium_crypto_secretbox_encrypt_password(uschar **yield, int argc, uschar *a unsigned int outputsize = sodium_base64_ENCODED_LEN(combined_message_len, sodium_base64_VARIANT_ORIGINAL); unsigned char *outstring = (unsigned char *) store_get(outputsize); - sodium_memzero(outstring, outputsize); + //sodium_memzero(outstring, outputsize); sodium_bin2base64((char *const) outstring, outputsize, combined_message, combined_message_len, sodium_base64_VARIANT_ORIGINAL); @@ -71,26 +88,27 @@ int sodium_crypto_secretbox_encrypt_password(uschar **yield, int argc, uschar *a } /* - * Decrypt the second argument with password string from the first argument - * using crypto_secretbox_open_easy(). + * Decrypt message encrypted by sodium_crypto_secretbox_encrypt_password(). Arguments: + * 1. password string + * 2. encrypted message */ -int sodium_crypto_secretbox_decrypt_password(uschar ** yield, int argc, uschar * argv[]) -{ +int sodium_crypto_secretbox_decrypt_password(uschar **yield, int argc, uschar *argv[]) { // ensure libsodium is initialized if (sodium_init() == -1) { - *yield = string_copy(US "Unable to initialize libsodium"); + *yield = string_copy(US + "Unable to initialize libsodium"); return ERROR; } // check argument count if (argc != 2) { *yield = string_sprintf - ("Wrong number of arguments (got %i, expected 2)", argc); + ("Wrong number of arguments (got %i, expected 2)", argc); return ERROR; } // get password unsigned char *password = argv[0]; - size_t passwordlen = strlen((const char *)password); + size_t passwordlen = strlen((const char *) password); // Derive key from the password. unsigned char keybytes[crypto_secretbox_KEYBYTES]; @@ -106,10 +124,14 @@ int sodium_crypto_secretbox_decrypt_password(uschar ** yield, int argc, uschar * unsigned int combined_message_len = ciphertextb64len / 4 * 3; unsigned char *combined_message = (unsigned char *) store_get(combined_message_len); sodium_memzero(combined_message, combined_message_len); - sodium_base642bin(combined_message, combined_message_len, + int b64err = sodium_base642bin(combined_message, combined_message_len, (const char *) ciphertextb64, ciphertextb64len, NULL, NULL, NULL, sodium_base64_VARIANT_ORIGINAL); + if (b64err != 0) { + *yield = string_copy((unsigned char *) "Error decoding base64 encoded ciphertext"); + return ERROR; + } // extract nonce unsigned char nonce[crypto_secretbox_NONCEBYTES]; @@ -117,17 +139,142 @@ int sodium_crypto_secretbox_decrypt_password(uschar ** yield, int argc, uschar * // prepare buffer for cleartext unsigned int cleartextlen = combined_message_len - crypto_secretbox_NONCEBYTES - crypto_secretbox_MACBYTES; - unsigned char *cleartext = (unsigned char *) store_get(cleartextlen+1); - sodium_memzero(cleartext, cleartextlen+1); + unsigned char *cleartext = (unsigned char *) store_get(cleartextlen + 1); + sodium_memzero(cleartext, cleartextlen + 1); // decrypt message if (crypto_secretbox_open_easy(cleartext, &combined_message[crypto_secretbox_NONCEBYTES], combined_message_len - crypto_secretbox_NONCEBYTES, nonce, keybytes) != 0) { - *yield = string_copy((unsigned char *) "Unable to decrypt."); + *yield = string_copy((unsigned char *) "Decryption error after crypto_secretbox_open_easy()"); return ERROR; } - // return base64-encoded ciphertext + // return cleartext *yield = string_copy(cleartext); return OK; } + +// -------------------------------- + + +/* + * Encrypt message using crypto_box_seal(). + * + * Arguments: + * 1. public key + * 2. cleartext message + */ +int sodium_crypto_box_seal(uschar **yield, int argc, uschar *argv[]) { + if (sodium_init() == -1) { + *yield = string_copy(US + "Unable to initialize libsodium"); + return ERROR; + } + if (argc != 2) { + *yield = string_sprintf("Wrong number of arguments (got %i, expected 2)", argc); + return ERROR; + } + + // get key, check size + unsigned char *key = argv[0]; + size_t keylen = strlen((const char *) key); + if (keylen != crypto_box_PUBLICKEYBYTES) { + *yield = string_sprintf("Public key has wrong size (got %d, expected %d)", keylen, crypto_box_PUBLICKEYBYTES); + return ERROR; + }; + + // get cleartext message + unsigned char *message = argv[1]; + size_t messagelen = strlen((const char *) message); + + // prepare buffer for ciphertext + unsigned int cipherlen = messagelen + crypto_box_SEALBYTES; + unsigned char *ciphertext = store_get(cipherlen); + sodium_memzero(ciphertext, cipherlen); + + // encrypt message + if (crypto_box_seal(ciphertext, message, messagelen, key) == -1) { + *yield = string_copy((unsigned char *) "Encryption error after crypto_box_seal()"); + return ERROR; + } + + // base64-encode the ciphertext + unsigned int outputsize = sodium_base64_ENCODED_LEN(cipherlen, + sodium_base64_VARIANT_ORIGINAL); + unsigned char *outstring = store_get(outputsize); + sodium_bin2base64((char *const) outstring, outputsize, + ciphertext, cipherlen, + sodium_base64_VARIANT_ORIGINAL); + + // return base64-encoded ciphertext + *yield = string_copy(outstring); + return OK; +} + +/* + * Decrypt message encrypted by sodium_crypto_box_seal(). + * + * Arguments: + * 1. private key + * 2. public key + * 3. encrypted message + */ +int sodium_crypto_box_seal_open(uschar **yield, int argc, uschar *argv[]) { + if (sodium_init() == -1) { + *yield = string_copy(US + "Unable to initialize libsodium"); + return ERROR; + } + if (argc != 3) { + *yield = string_sprintf("Wrong number of arguments (got %i, expected 2)", argc); + return ERROR; + } + + // check private key + unsigned char *sk = argv[0]; + size_t sk_len = strlen((const char *)sk); + if (sk_len != crypto_box_SECRETKEYBYTES) { + *yield = string_sprintf("Private key has wrong size (got %d, expected %d)", sk_len, crypto_box_SECRETKEYBYTES); + return ERROR; + } + + // check public key + unsigned char *pk = argv[1]; + size_t pk_len = strlen((const char *)pk); + if (pk_len != crypto_box_PUBLICKEYBYTES) { + *yield = string_sprintf("Private key has wrong size (got %d, expected %d)", pk_len, crypto_box_PUBLICKEYBYTES); + return ERROR; + } + + // get encrypted message + unsigned char *ciphertextb64 = argv[2]; + size_t ciphertextb64len = strlen((const char *) ciphertextb64); + + // base64-decode the ciphertext + unsigned int ciphertextlen = ciphertextb64len / 4 * 3; + unsigned char *ciphertext = (unsigned char *) store_get(ciphertextlen); + sodium_memzero(ciphertext, ciphertextlen); + int b64err = sodium_base642bin(ciphertext, ciphertextlen, + (const char *) ciphertextb64, ciphertextb64len, + NULL, NULL, NULL, + sodium_base64_VARIANT_ORIGINAL); + if (b64err != 0) { + *yield = string_copy((unsigned char *) "Error decoding base64 encoded ciphertext"); + return ERROR; + } + + // prepare buffer for cleartext + unsigned int cleartextlen = ciphertextlen - crypto_box_SEALBYTES; + unsigned char *cleartext = (unsigned char *) store_get(cleartextlen + 1); + sodium_memzero(cleartext, cleartextlen + 1); + + // decrypt message + if (crypto_box_seal_open(cleartext, ciphertext, ciphertextlen, pk, sk) != 0) { + *yield = string_copy((unsigned char *) "Decryption error after crypto_box_seal_open()"); + return ERROR; + } + + // return cleartext + *yield = string_copy(cleartext); + return OK; +} \ No newline at end of file diff --git a/src/libexim-kitencrypt-dlfunc.c b/src/libexim-kitencrypt-dlfunc.c deleted file mode 100644 index 186d8e7..0000000 --- a/src/libexim-kitencrypt-dlfunc.c +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include - -/* Exim4 dlfunc API header */ -#include - -/* - * Encrypt the second argument with the public key from the first argument - */ - -/* -int sodium_crypto_box_seal(uschar ** yield, int argc, uschar * argv[]) -{ - size_t messagelen; - unsigned char *message, *key; - - if (sodium_init() == -1) { - *yield = string_copy(US "Unable to initialize libsodium"); - return ERROR; - } - if (argc != 2) { - *yield = - string_sprintf - ("Wrong number of arguments (got %i, expected 2)", argc); - return ERROR; - } - // get cleartext message - message = argv[0]; - messagelen = strlen((const char *)message); - - // get key - key = argv[1]; - - // prepare buffer for ciphertext - unsigned int cipherlen = messagelen + crypto_box_SEALBYTES; - unsigned char *ciphertext = malloc(cipherlen); - sodium_memzero(ciphertext, cipherlen); - - // encrypt message - crypto_box_seal(ciphertext, message, messagelen, recipient_pk); - - // base64-encode the ciphertext - unsigned int outputsize = sodium_base64_ENCODED_LEN(cipherlen, - sodium_base64_VARIANT_ORIGINAL); - unsigned char *outstring = malloc(outputsize); - sodium_memzero(outstring, outputsize); - - sodium_bin2base64((char *const)outstring, outputsize, - ciphertext, cipherlen, - sodium_base64_VARIANT_ORIGINAL); - free(ciphertext); - - // return base64-encoded ciphertext - *yield = string_copy(outstring); - free(outstring); - - return OK; -} -*/ \ No newline at end of file diff --git a/src/simple_exim_test.sh b/src/simple_exim_test.sh index fdd2092..4662b1b 100755 --- a/src/simple_exim_test.sh +++ b/src/simple_exim_test.sh @@ -8,3 +8,8 @@ CIPHERTEXT=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_encrypt_passwor DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_decrypt_password}{${PASSWORD}}{${CIPHERTEXT}}}") if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then echo "Test successful"; fi + + +exim -be "cryptobox_recipient_pk = \"\xa0\x32\xf6\xa8\x2a\x6c\x7b\x41\xf4\xfc\xc8\x7c\xa8\x79\x46\x24\x9c\x03\xf7\x47\xf4\x6f\x6c\xea\x90\xcb\xed\x4c\x48\x4d\x57\x63\" \\ +cryptobox_recipient_sk = \"\xf7\x4d\x7d\x26\x90\x6a\x63\xc3\xfa\xcd\xed\x2f\xd4\xd0\x6a\x76\xf5\xc4\xe6\xd8\xcb\xcf\x4c\x0e\x2d\x7c\x99\xfd\xcb\x0b\xf5\xfe\" \\ +\${dlfunc{${LIB}}{sodium_crypto_box_seal}{cryptobox_recipient_pk}{${CLEARTEXT}}}"