Merge branch 'decryption_tools' into 'main'

Add decryption tools

Closes #1

See merge request mail/exim-encrypt-dlfunc!4
This commit is contained in:
heiko.reese
2021-09-12 02:47:42 +02:00
15 changed files with 694 additions and 40 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/build /build
debian/*.ex

View File

@ -21,8 +21,10 @@ stages:
- cd .. - cd ..
artifacts: artifacts:
paths: paths:
- build/src/generate_encryption_keys
- build/src/libexim-encrypt-dlfunc.so - build/src/libexim-encrypt-dlfunc.so
- build/src/libexim-encrypt-dlfunc-genkeys
- build/src/libexim-encrypt-dlfunc-decrypt-secretbox
#- build/src/libexim-encrypt-dlfunc-decrypt-sealedtbox
.debian-package: .debian-package:
stage: debian-package stage: debian-package
@ -37,31 +39,23 @@ stages:
- ./*.changes - ./*.changes
- ./*.buildinfo - ./*.buildinfo
build:buster:
extends:
- .build
- .image-buster
needs: []
build:bullseye: build:bullseye:
extends: extends:
- .image-bullseye - .image-bullseye
- .build - .build
needs: [] needs: [ ]
build:focal: build:focal:
extends: extends:
- .image-focal - .image-focal
- .build - .build
needs: [] needs: [ ]
debian-package:buster: build:buster:
extends: extends:
- .build
- .image-buster - .image-buster
- .debian-package needs: [ ]
dependencies:
- build:buster
needs: ["build:buster"]
debian-package:bullseye: debian-package:bullseye:
extends: extends:
@ -69,7 +63,7 @@ debian-package:bullseye:
- .debian-package - .debian-package
dependencies: dependencies:
- build:bullseye - build:bullseye
needs: ["build:bullseye"] needs: [ "build:bullseye" ]
debian-package:focal: debian-package:focal:
extends: extends:
@ -77,4 +71,12 @@ debian-package:focal:
- .debian-package - .debian-package
dependencies: dependencies:
- build:focal - build:focal
needs: ["build:focal"] needs: [ "build:focal" ]
debian-package:buster:
extends:
- .image-buster
- .debian-package
dependencies:
- build:buster
needs: [ "build:buster" ]

View File

@ -44,7 +44,10 @@ meson compile -C build
meson test -C build meson test -C build
``` ```
5. Copy to final destination (feel free to pick another place than `/usr/local/lib/`): The `ci_container` directory contains a [script](ci_container/build.sh) (and a [short README](ci_container/README.md))
which creates the images used in continous integration for this project.
5. Copy to final destination (feel free to pick another place than `/usr/lib/x86_64-linux-gnu/`):
```shell ```shell
meson install -C build meson install -C build
@ -54,9 +57,18 @@ meson install -C build
Not every build of exim is able to load libraries at runtime. Please refer to the Not every build of exim is able to load libraries at runtime. Please refer to the
[documentation](https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html) [documentation](https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html)
of the `${dlfunc{…}}` function for details. The Debian package [`exim4-daemon-heavy`](https://packages.debian.org/exim4-daemon-heavy) of the `${dlfunc{…}}` function for details. The Debian
package [`exim4-daemon-heavy`](https://packages.debian.org/exim4-daemon-heavy)
meets these requirements. meets these requirements.
Try
```shell
exim4 --version | egrep -i --color 'Expand_dlfunc|Content_Scanning'
```
for a preliminary test.
## Usage ## Usage
There are currently two pairs of complementary functions: There are currently two pairs of complementary functions:
@ -74,11 +86,11 @@ Public key encryption that uses a key pair which needs to be created beforehand:
* `sodium_crypto_box_seal_open(private key, public key, ciphertext) → cleartext` * `sodium_crypto_box_seal_open(private key, public key, ciphertext) → cleartext`
The second pair needs a proper key pair in the correct format. This is what the The second pair needs a proper key pair in the correct format. This is what the
`generate_encryption_keys` utility is for. Simply run it once to generate a pair. Be aware that every invocation will `libexim-encrypt-dlfunc-genkeys` utility is for. Simply run it once to generate a pair. Be aware that every invocation will
overwrite the previous key pair file without confirmation! Make sure to store your production keys in a safe place. overwrite the previous key pair file without confirmation! Make sure to store your production keys in a safe place.
```shell ```shell
$ ./generate_encryption_keys $ libexim-encrypt-dlfunc-genkeys
=== Creating cryptobox key pair === === Creating cryptobox key pair ===
Wrote »cryptobox_recipient_pk_exim.conf« Wrote »cryptobox_recipient_pk_exim.conf«
Wrote »cryptobox_recipient_pk.raw« Wrote »cryptobox_recipient_pk.raw«
@ -107,11 +119,11 @@ original header is usually provided in these cases). Add this snippet to your DA
``` ```
warn log_message = Removing X-Originating-IP: header warn log_message = Removing X-Originating-IP: header
condition = ${if def:h_X-originating-IP: {1}{0}} condition = ${if def:h_X-originating-IP: {1}{0}}
add_header = X-Orig-IP-PKK: ${dlfunc{/usr/local/lib/libexim-encrypt-dlfunc.so} \ add_header = X-Orig-IP-PKK: ${dlfunc{/usr/lib/x86_64-linux-gnu/libexim-encrypt-dlfunc.so} \
{sodium_crypto_box_seal} \ {sodium_crypto_box_seal} \
{ktp1OEEItrgvSfpVTtu+ybyNjzuuN8OzCdfrGAJt4j8=} \ {ktp1OEEItrgvSfpVTtu+ybyNjzuuN8OzCdfrGAJt4j8=} \
{$h_X-originating-IP:}} {$h_X-originating-IP:}}
add_header = X-Orig-IP-Pass: ${dlfunc{/usr/local/lib/libexim-encrypt-dlfunc.so} \ add_header = X-Orig-IP-Pass: ${dlfunc{/usr/lib/x86_64-linux-gnu/libexim-encrypt-dlfunc.so} \
{sodium_crypto_secretbox_encrypt_password} \ {sodium_crypto_secretbox_encrypt_password} \
{Insert your password here} \ {Insert your password here} \
{$h_X-originating-IP:}} {$h_X-originating-IP:}}
@ -120,3 +132,55 @@ warn log_message = Removing X-Originating-IP: header
``` ```
Pick one of the `add_header` lines depending on which kind of encryption you want. Pick one of the `add_header` lines depending on which kind of encryption you want.
### Decryption Tools
Two additional programs are included:
* `libexim-encrypt-dlfunc-decrypt-secretbox`
* `libexim-encrypt-dlfunc-decrypt-sealedbox`
They can decrypt strings that were encrypted by the two respective functions. Please refer to their `--help` message
(reproduced below) for usage information and to the [test](src/test_libexim-encrypt-dlfunc-decrypt-secretbox.sh)
[scripts](src/test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh) for usage examples.
```shell
$ libexim-encrypt-dlfunc-decrypt-secretbox -h
Usage: libexim-encrypt-dlfunc-decrypt-secretbox [OPTIONS] [CIPHERTEXT]
Password:
-p, --password PASSWORD Decrypt using PASSWORD
If the environment variable LIBEXIM_PASSWORD is set the password is read from there.
Setting a password with -p/--password overwrites this mechanism.
Select input:
-f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)
Output:
-n, --no-newline Do not append a newline to the output
Password and ciphertext are expected to be base64-encoded (as produced by the library).
```
```shell
$ libexim-encrypt-dlfunc-decrypt-sealedbox -h
Usage: libexim-encrypt-dlfunc-decrypt-sealedbox [OPTIONS] [CIPHERTEXT]
Secret and public key:
-s, --secret-key SECRETKEY Secret key (base64-encoded)
-p, --public-key PUBLICKEY Public key (base64-encoded)
-S, --secret-key-file FILE Read secret key (raw) from file FILE (use - for stdin)
-P, --public-key-file FILE Read public key (raw) from file FILE (use - for stdin)
The environment variables LIBEXIM_SECRETKEY and LIBEXIM_PUBLICKEY may contain base64-encoded secret/public keys.
Select input:
-f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)
Output:
-n, --no-newline Do not append a newline to the output
Keys in arguments and environment variables are expected to be base64 encoded (as produced by the library).
Keys in files need to be raw bytes with no encoding, ciphertext should always be base64-encoded.
```

View File

@ -3,7 +3,7 @@
## Prerequisites ## Prerequisites
* [buildah](https://buildah.io/) * [buildah](https://buildah.io/)
* {podman](https://podman.io/) * [podman](https://podman.io/)
## Build and upload ## Build and upload

View File

@ -61,11 +61,14 @@ for i in "${images[@]}"; do
openssl; \ openssl; \
DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install -y \ DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install -y \
debhelper \ debhelper \
dh-make \
devscripts \
git-buildpackage \
debsigs \ debsigs \
gpgv1; \ devscripts \
dh-make \
git-buildpackage \
gpgv1 \
meson; \
DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install -y \
vim; \
rm -rf /var/lib/apt/lists/*;' rm -rf /var/lib/apt/lists/*;'
buildah run "$ctr" /bin/sh -c \ buildah run "$ctr" /bin/sh -c \
'pip3 install meson ninja; \ 'pip3 install meson ninja; \

89
src/common.c Normal file
View File

@ -0,0 +1,89 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sodium.h>
#include "common.h"
//#define MIN_KEY_SIZE (crypto_box_SECRETKEYBYTES < crypto_box_PUBLICKEYBYTES ? crypto_box_SECRETKEYBYTES : crypto_box_PUBLICKEYBYTES)
//#define MAX_KEY_SIZE (crypto_box_SECRETKEYBYTES > crypto_box_PUBLICKEYBYTES ? crypto_box_SECRETKEYBYTES : crypto_box_PUBLICKEYBYTES)
char *read_first_line(const char *filename) {
FILE *stream;
char *cipherstring;
size_t len = 0;
ssize_t nread;
bool input_is_stdin = false;
// open file (use stdin for '-')
if (!strncmp(filename, "-", 1)) {
stream = stdin;
input_is_stdin = true;
} else {
stream = fopen(filename, "r");
if (stream == NULL) {
fprintf(stderr, "[ERROR] Error opening file %s\n\n", filename);
exit(EXIT_FAILURE);
}
}
nread = getline(&cipherstring, &len, stream);
if (nread == -1 && !feof(stream)) {
perror("getline: ");
}
if (input_is_stdin == false) {
fclose(stream);
}
// remove trailing newline
cipherstring[strcspn(cipherstring, "\r\n")] = 0;
return cipherstring;
}
char *read_password_file(const char *filename, size_t keysize, size_t *length) {
FILE *stream;
char *contents;
ssize_t nread;
bool input_is_stdin = false;
contents = malloc(keysize + 1);
sodium_memzero(contents, keysize + 1);
// open file (use stdin for '-')
if (!strncmp(filename, "-", 1)) {
stream = stdin;
input_is_stdin = true;
} else {
stream = fopen(filename, "r");
if (stream == NULL) {
fprintf(stderr, "[ERROR] Error opening file %s\n\n", filename);
exit(EXIT_FAILURE);
}
}
nread = fread(contents, sizeof(char), keysize, stream);
if (nread < 0) {
fprintf(stderr, "[ERROR] reading from %s failed\n\n", filename);
exit(EXIT_FAILURE);
} else {
*length = (size_t) nread;
}
if (input_is_stdin == false) {
fclose(stream);
}
return contents;
}
int base64_decode_string(const char *input, unsigned char **outstring, size_t *outlen) {
size_t input_len = strlen(input);
size_t outmaxlen = input_len / 4 * 3;
*outstring = malloc(outmaxlen * sizeof(unsigned char));
return sodium_base642bin(*outstring, outmaxlen, (const char *) input, input_len,
NULL, outlen, NULL, sodium_base64_VARIANT_ORIGINAL);
}

14
src/common.h Normal file
View File

@ -0,0 +1,14 @@
//
// Created by sprawl on 08/09/2021.
//
#ifndef EXIM_ENCRYPT_DLFUNC_COMMON_H
#define EXIM_ENCRYPT_DLFUNC_COMMON_H
char *read_first_line(const char *filename);
char *read_password_file(const char *filename, size_t keysize, size_t *length);
int base64_decode_string(const char *input, unsigned char **outstring, size_t *outlen);
#endif //EXIM_ENCRYPT_DLFUNC_COMMON_H

View File

@ -20,7 +20,7 @@ char *string2hex(unsigned char *input, size_t length) {
* 1. Add this code to the first “breakpoint”: * 1. Add this code to the first “breakpoint”:
* log_write(0, LOG_MAIN, "pid: %d", getpid()); int busywait = 0; while (busywait == 0) {} * log_write(0, LOG_MAIN, "pid: %d", getpid()); int busywait = 0; while (busywait == 0) {}
* 2. Compile. * 2. Compile.
* 3. Run “exim -be […]” to call the lib; see simple_exim_test.sh for details. * 3. Run “exim -be […]” to call the lib; see test_libexim-encrypt-dlfunc.sh for details.
* 4. Read exim pid from log output. Attach to the looping exim process with “gdb -p PID” * 4. Read exim pid from log output. Attach to the looping exim process with “gdb -p PID”
* 5. Prepare breakpoints, watches, etc. Set busywait to 1 and continue. * 5. Prepare breakpoints, watches, etc. Set busywait to 1 and continue.
*/ */

View File

@ -0,0 +1,203 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sodium.h>
#include <stdbool.h>
#include "common.c"
#define ENVVAR_SK_NAME "LIBEXIM_SECRETKEY"
#define ENVVAR_PK_NAME "LIBEXIM_PUBLICKEY"
void print_usage(char *progname) {
printf("Usage: %s [OPTIONS] [CIPHERTEXT]\n\n", progname);
printf("Secret and public key:\n");
printf(" -s, --secret-key SECRETKEY Secret key (base64-encoded)\n");
printf(" -p, --public-key PUBLICKEY Public key (base64-encoded)\n");
printf(" -S, --secret-key-file FILE Read secret key (raw) from file FILE (use - for stdin)\n");
printf(" -P, --public-key-file FILE Read public key (raw) from file FILE (use - for stdin)\n");
printf("\n");
printf("The environment variables %s and %s may contain base64-encoded secret/public keys. \n", ENVVAR_SK_NAME,
ENVVAR_PK_NAME);
printf("\n");
printf("Select input:\n");
printf(" -f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)\n");
printf("\n");
printf("Output:\n");
printf(" -n, --no-newline Do not append a newline to the output\n");
printf("\n");
printf("Keys in arguments and environment variables are expected to be base64 encoded (as produced by the library).\n");
printf("Keys in files need to be raw bytes with no encoding, ciphertext should always be base64-encoded.\n");
printf("\n");
}
typedef enum {
NONE = 0,
SK = 1,
PK = 2,
SKENV = 4,
PKENV = 8,
SKFILE = 16,
PKFILE = 32,
INFILE = 64,
INSTRING = 128
} seen_sb_args;
int main(int argc, char *argv[]) {
char *prog_basename = basename(argv[0]);
int opt = 0;
unsigned char *secretkey, *publickey;
size_t secretkey_len = 0, publickey_len = 0;
char *b64cipherstring;
unsigned char *cipherstring;
size_t cipherstring_len;
bool add_newline = true;
seen_sb_args publickey_mode = NONE;
seen_sb_args secretkey_mode = NONE;
seen_sb_args input = NONE;
if (sodium_init() < 0) {
fputs("Unable to initialize libsodium", stderr);
exit(128);
}
// define arguments
const char *shortargs = "s:p:S:P:f:nh";
static struct option long_options[] = {
{"secret-key", required_argument, NULL, 's'},
{"public-key", required_argument, NULL, 'p'},
{"secret-key-file", required_argument, NULL, 'S'},
{"public-key-file", required_argument, NULL, 'P'},
{"infile", required_argument, NULL, 'f'},
{"no-newline", required_argument, NULL, 'n'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0}
};
// check environment for LIBEXIM_SECRETKEY variable
char *sk_env = getenv(ENVVAR_SK_NAME);
if (sk_env != NULL && strlen(sk_env) > 0) {
if (base64_decode_string(sk_env, &secretkey, &secretkey_len) != 0) {
fprintf(stderr, "[ERROR] Unable to base64-decode secret key.\n\n");
exit(EXIT_FAILURE);
}
secretkey_mode |= SKENV;
}
// check environment for LIBEXIM_PUBLICKEY variable
char *pk_env = getenv(ENVVAR_PK_NAME);
if (pk_env != NULL && strlen(pk_env) > 0) {
if (base64_decode_string(pk_env, &publickey, &publickey_len) != 0) {
fprintf(stderr, "[ERROR] Unable to base64-decode public key.\n\n");
exit(EXIT_FAILURE);
}
publickey_mode |= PKENV;
}
// parse arguments
int long_index = 0;
while ((opt = getopt_long(argc, argv, shortargs,
long_options, &long_index)) != -1) {
switch (opt) {
case 's':
if (base64_decode_string(optarg, &secretkey, &secretkey_len) != 0) {
fprintf(stderr, "[ERROR] Unable to base64-decode secret key.\n\n");
exit(EXIT_FAILURE);
}
secretkey_mode |= SK;
break;
case 'p':
if (base64_decode_string(optarg, &publickey, &publickey_len) != 0) {
fprintf(stderr, "[ERROR] Unable to base64-decode public key.\n\n");
exit(EXIT_FAILURE);
}
publickey_mode |= PK;
break;
case 'S':
secretkey = (unsigned char *) read_password_file(optarg, crypto_box_SECRETKEYBYTES, &secretkey_len);
secretkey_mode |= SKFILE;
break;
case 'P':
publickey = (unsigned char *) read_password_file(optarg, crypto_box_PUBLICKEYBYTES, &publickey_len);
publickey_mode |= PKFILE;
break;
case 'f':
b64cipherstring = read_first_line(optarg);
input |= INFILE;
break;
case 'n':
add_newline = false;
break;
case 'h':
print_usage(prog_basename);
exit(EXIT_SUCCESS);
break;
}
}
// read first non-option argument as ciphertext if present
if (optind < argc) {
size_t b64cipherstring_len = strlen(argv[optind]);
b64cipherstring = malloc(b64cipherstring_len);
sodium_memzero(b64cipherstring, b64cipherstring_len);
strncpy(b64cipherstring, argv[optind], b64cipherstring_len);
input |= INSTRING;
}
// check if a secret key was provided
if (secretkey_mode == NONE) {
fprintf(stderr, "[ERROR] Please specify a secret key.\n\n");
print_usage(prog_basename);
exit(EXIT_FAILURE);
}
// check if the secret key has the correct size
if (secretkey_len != crypto_box_SECRETKEYBYTES) {
fprintf(stderr, "[ERROR] Secret key has wrong size %zu; expected %d.\n\n", secretkey_len,
crypto_box_SECRETKEYBYTES);
exit(EXIT_FAILURE);
}
// check if a public key was provided
if (publickey_mode == NONE) {
fprintf(stderr, "[ERROR] Please specify a public key.\n\n");
print_usage(prog_basename);
exit(EXIT_FAILURE);
}
// check if the public key has the correct size
if (publickey_len != crypto_box_PUBLICKEYBYTES) {
fprintf(stderr, "[ERROR] Secret key has wrong size %zu; expected %d.\n\n", publickey_len,
crypto_box_PUBLICKEYBYTES);
exit(EXIT_FAILURE);
}
// check if a ciphertext was provided
if (input == NONE) {
fprintf(stderr, "[ERROR] Please specify a ciphertext source.\n\n");
print_usage(prog_basename);
exit(EXIT_FAILURE);
}
// base64-decode ciphertext
if (base64_decode_string(b64cipherstring, &cipherstring, &cipherstring_len) != 0) {
fprintf(stderr, "[ERROR] Unable to base64-decode ciphertext.\n\n");
exit(EXIT_FAILURE);
}
// prepare buffer for cleartext
size_t cleartext_len = cipherstring_len - crypto_box_SEALBYTES;
unsigned char *cleartext = (unsigned char *) malloc(cleartext_len + 1);
sodium_memzero(cleartext, cleartext_len + 1);
// decrypt message
if (crypto_box_seal_open(cleartext, cipherstring, cipherstring_len, publickey, secretkey) != 0) {
fprintf(stderr, "[ERROR] Unable to decrypt ciphertext.\n\n");
exit(EXIT_FAILURE);
}
// print cleartext to stdout
if (add_newline == true) {
fprintf(stdout, "%s\n", (const char *) cleartext);
} else {
fprintf(stdout, "%s", (const char *) cleartext);
}
}

View File

@ -0,0 +1,161 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <sodium.h>
#include <stdbool.h>
#include "common.c"
#define ENVVAR_PASSWORD_NAME "LIBEXIM_PASSWORD"
void print_usage(char *progname) {
printf("Usage: %s [OPTIONS] [CIPHERTEXT]\n\n", progname);
printf("Password:\n");
printf(" -p, --password PASSWORD Decrypt using PASSWORD\n");
printf("\n");
printf(" If the environment variable LIBEXIM_PASSWORD is set the password is read from there.\n");
printf(" Setting a password with -p/--password overwrites this mechanism.\n");
printf("\n");
printf("Select input:\n");
printf(" -f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)\n");
printf("\n");
printf("Output:\n");
printf(" -n, --no-newline Do not append a newline to the output\n");
printf("\n");
printf("Password and ciphertext are expected to be base64-encoded (as produced by the library).\n");
printf("\n");
}
typedef enum {
NONE = 0,
PASSARG = 1,
PASSENV = 2,
INSTRING = 4,
INFILE = 8
} seen_args;
int main(int argc, char *argv[]) {
char *prog_basename = basename(argv[0]);
int opt;
char *cipherstring;
size_t password_len;
char *password;
char *password_env;
bool add_newline = true;
seen_args mode = NONE;
seen_args input = NONE;
if (sodium_init() < 0) {
fputs("Unable to initialize libsodium", stderr);
exit(128);
}
// define arguments
const char *shortargs = "p:f:nh";
static struct option long_options[] = {
{"password", required_argument, NULL, 'p'},
{"infile", required_argument, NULL, 'f'},
{"no-newline", no_argument, NULL, 'n'},
{"help", no_argument, NULL, 'h'},
{0, 0, 0, 0}
};
// check environment for LIBEXIM_PASSWORD
password_env = getenv(ENVVAR_PASSWORD_NAME);
if (password_env != NULL && strlen(password_env) > 0) {
password = password_env;
password_len = strlen(password);
mode |= PASSENV;
}
// parse arguments
int long_index = 0;
while ((opt = getopt_long(argc, argv, shortargs,
long_options, &long_index)) != -1) {
switch (opt) {
case 'p':
password_len = strlen((const char *) optarg);
password = malloc(password_len + 1);
strncpy(password, optarg, password_len);
mode |= PASSARG;
break;
case 'f':
cipherstring = read_first_line(optarg);
input |= INFILE;
break;
case 'n':
add_newline = false;
break;
case 'h':
print_usage(prog_basename);
exit(EXIT_SUCCESS);
break;
}
}
// read first non-option argument as ciphertext if present
if (optind < argc) {
size_t cipherstring_len = strlen(argv[optind]) + 1;
cipherstring = malloc(cipherstring_len + 1);
strncpy(cipherstring, argv[optind], cipherstring_len);
input |= INSTRING;
}
// check if a password was provided
if (mode == NONE) {
fprintf(stderr, "[ERROR] Please specify a password.\n\n");
print_usage(prog_basename);
exit(EXIT_FAILURE);
}
// fail if neither argument nor filename for ciphertext is present
if (input == NONE) {
fprintf(stderr, "[ERROR] Please specify a ciphertext source.\n\n");
print_usage(prog_basename);
exit(EXIT_FAILURE);
}
// Derive key from the password.
unsigned char keybytes[crypto_secretbox_KEYBYTES];
sodium_memzero(keybytes, crypto_secretbox_KEYBYTES);
crypto_generichash(keybytes, crypto_secretbox_KEYBYTES,
(unsigned char *) password, password_len, NULL, 0);
// base64-decode input
unsigned char *ciphertext;
size_t ciphertext_len;
if (base64_decode_string(cipherstring, &ciphertext, &ciphertext_len) != 0) {
fprintf(stderr, "[ERROR] Unable to base64-decode ciphertext.\n\n");
exit(EXIT_FAILURE);
};
// extract nonce
unsigned char nonce[crypto_secretbox_NONCEBYTES];
memcpy(nonce, ciphertext, crypto_secretbox_NONCEBYTES);
// prepare buffer for cleartext
if (ciphertext_len < crypto_secretbox_NONCEBYTES + crypto_secretbox_MACBYTES) {
fprintf(stderr, "[ERROR] Ciphertext is too small to contain any data.\n\n");
exit(EXIT_FAILURE);
}
size_t cleartext_len = ciphertext_len - crypto_secretbox_NONCEBYTES - crypto_secretbox_MACBYTES;
unsigned char *cleartext = (unsigned char *) malloc(cleartext_len + 1);
sodium_memzero(cleartext, cleartext_len + 1);
// decrypt
if (crypto_secretbox_open_easy(cleartext, &ciphertext[crypto_secretbox_NONCEBYTES],
ciphertext_len - crypto_secretbox_NONCEBYTES, nonce, keybytes) == 0) {
} else {
fprintf(stderr, "[ERROR] Unable to decrypt message.\n\n");
exit(EXIT_FAILURE);
}
// print cleartext to stdout
if (add_newline == true) {
fprintf(stdout, "%s\n", (const char *) cleartext);
} else {
fprintf(stdout, "%s", (const char *) cleartext);
}
}

View File

@ -1,6 +1,14 @@
configure_file(output: 'config.h', configuration: conf_data) configure_file(output: 'config.h', configuration: conf_data)
executable('generate_encryption_keys', 'generate_encryption_keys.c', executable('libexim-encrypt-dlfunc-genkeys', 'libexim-encrypt-dlfunc-genkeys.c',
dependencies : [ sodium_deps ],
install: true)
executable('libexim-encrypt-dlfunc-decrypt-sealedbox', 'libexim-encrypt-dlfunc-decrypt-sealedbox.c',
dependencies : [ sodium_deps ],
install: true)
executable('libexim-encrypt-dlfunc-decrypt-secretbox', 'libexim-encrypt-dlfunc-decrypt-secretbox.c',
dependencies : [ sodium_deps ], dependencies : [ sodium_deps ],
install: true) install: true)
@ -8,7 +16,14 @@ shared_library('exim-encrypt-dlfunc', 'libexim-encrypt-dlfunc.c',
dependencies : [ sodium_deps ], dependencies : [ sodium_deps ],
install: true) install: true)
test('libexim-encrypt-dlfunc',
find_program('test_libexim-encrypt-dlfunc.sh'),
protocol: 'tap')
simple_exim_test = find_program('simple_exim_test.sh') test('decrypt-secretbox',
test('simple test', simple_exim_test) find_program('test_libexim-encrypt-dlfunc-decrypt-secretbox.sh'),
protocol: 'tap')
test('decrypt-sealedbox',
find_program('test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh'),
protocol: 'tap')

View File

@ -0,0 +1,46 @@
#!/bin/bash
# shellcheck disable=SC2034
# this script implements the TAP protocol (https://testanything.org)
echo 1..3
TEST_PUBLICKEY='z7+GlUWgoiXJ4VK6cdLikCF7M6Mj9i4eXNE6Jh1m9yw='
TEST_SECRETKEY='h5C/V2nzhILRmJ6UZrNK/6G8Xc4KWzLq/Qr8Xj42jus='
TEST_CLEARTEXT='There is nothing in the middle of the road but a yellow stripe and dead armadillos. ~ Jim Hightower'
TEST_CIPHERTEXT='+UgXgarCuX3dUobgt8rRnjnxPNWHpsw98GjLZy8+2m5e/v9K/acMq+0UsFW7lwAZIRqj1F55n78y73Y6XCBEVSt8G6nntV8WuDYlr1BHcBIXNr5toUbE+CxtLoGqfD3c3nw1NkJDO1NYGzK/cG43TEEBLrQJCRLRBXOZmxG6ugFo4FtYl297/B1xNtkd9IR4TY5C'
CIPHERTEXT_FILE="$(mktemp)"
TEST_PUBLICKEY_FILE="$(mktemp)"
TEST_SECRETKEY_FILE="$(mktemp)"
echo -n "${TEST_CIPHERTEXT}" > "${CIPHERTEXT_FILE}"
echo -n "${TEST_PUBLICKEY}" | base64 -d > "${TEST_PUBLICKEY_FILE}"
echo -n "${TEST_SECRETKEY}" | base64 -d > "${TEST_SECRETKEY_FILE}"
cleanup() {
rm -rf "${CIPHERTEXT_FILE}" "${TEST_PUBLICKEY_FILE}" "${TEST_SECRETKEY_FILE}"
}
trap cleanup EXIT INT TERM
export LIBEXIM_PUBLICKEY="${TEST_PUBLICKEY}"
export LIBEXIM_SECRETKEY="${TEST_SECRETKEY}"
DECRYPTED="$(src/libexim-encrypt-dlfunc-decrypt-sealedbox "${TEST_CIPHERTEXT}")"
if [ "${DECRYPTED}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 1 - decrypt commandline argument with keys from environment successful"
else
echo "not ok 1 - decrypt commandline argument with keys from environment unsuccessful"
fi
export -n LIBEXIM_PUBLICKEY LIBEXIM_SECRETKEY
DECRYPTED="$(src/libexim-encrypt-dlfunc-decrypt-sealedbox --secret-key "${TEST_SECRETKEY}" --public-key "${TEST_PUBLICKEY}" --infile "${CIPHERTEXT_FILE}")"
if [ "${DECRYPTED}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 2 - decrypt file contents with keys from commandline"
else
echo "not ok 2 - decrypt file contents with keys from commandline"
fi
DECRYPTED="$(src/libexim-encrypt-dlfunc-decrypt-sealedbox --secret-key-file "${TEST_SECRETKEY_FILE}" --public-key-file "${TEST_PUBLICKEY_FILE}" --infile - < "${CIPHERTEXT_FILE}")"
if [ "${DECRYPTED}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 3 - decrypt stdin contents with keys from files"
else
echo "not ok 3 - decrypt stdin contents with keys from files"
fi

View File

@ -0,0 +1,57 @@
#!/bin/bash
# shellcheck disable=SC2034
# this script implements the TAP protocol (https://testanything.org)
echo 1..5
TEST_PASSWORD='be6rahqu3bukee3Aengohgoopheeyis5'
TEST_CLEARTEXT='The great thing about attackers is that there are so many to choose from! - Daniel J. Bernstein'
TEST_CIPHERTEXT01='K+TOzrbkni7wydNvF1gMRwZWQPNnNIXRG9iQgkFhszBu8ImqIrAK4wWWP02UmclITi8DZbr3sg/EVWurDzAYK+pjkcDAa78glz4qXIqrPbYvEIEHPEExFzCtwi5hqOR+KF7tsqbPvdAOIqwf/2KBomX0GS1I/1CxQMrbJd1VgXc51M4hI0I8'
TEST_CIPHERTEXT02='lAod5UhfW6fQCxd4PSktnrzwyWzcw05Svio5XqPOr/p/Ts4Pr0eEjj2TgmT2K85T2xrxCiqmE/OUcODRldEWeSqBSxx0Z6PzXqOzz5ZL6Iq1tggjihMydGz9mNS4jRF9f52k5t2i7xFrMMCRrfq/rer/ngp1h3pposCds+OmX0u+1f4Urj0b'
CIPHERTEXT_FILE01="$(mktemp)"
CIPHERTEXT_FILE02="$(mktemp)"
echo -n "${TEST_CIPHERTEXT01}" > "${CIPHERTEXT_FILE01}"
echo -n "${TEST_CIPHERTEXT02}" > "${CIPHERTEXT_FILE02}"
cleanup() {
rm -rf "${CIPHERTEXT_FILE01}" "${CIPHERTEXT_FILE02}"
}
trap cleanup EXIT INT TERM
export LIBEXIM_PASSWORD="${TEST_PASSWORD}"
DECRYPTED01="$(src/libexim-encrypt-dlfunc-decrypt-secretbox ${TEST_CIPHERTEXT01})"
if [ "${DECRYPTED01}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 1 - decrypt commandline argument with password from environment successful"
else
echo "not ok 1 - decrypt commandline argument with password from environment unsuccessful"
fi
DECRYPTED02="$(src/libexim-encrypt-dlfunc-decrypt-secretbox --infile ${CIPHERTEXT_FILE01})"
if [ "${DECRYPTED02}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 2 - decrypt file contents with password from environment successful"
else
echo "not ok 2 - decrypt file contents with password from environment unsuccessful"
fi
DECRYPTED03="$(echo -n ${TEST_CIPHERTEXT01} | src/libexim-encrypt-dlfunc-decrypt-secretbox --infile -)"
if [ "${DECRYPTED03}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 3 - decrypt stdin contents with password from environment successful"
else
echo "not ok 3 - decrypt stdin file contents with password from environment unsuccessful"
fi
export -n LIBEXIM_PASSWORD
DECRYPTED04="$(src/libexim-encrypt-dlfunc-decrypt-secretbox -p ${TEST_PASSWORD} ${TEST_CIPHERTEXT02})"
if [ "${DECRYPTED04}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 4 - decrypt commandline argument with password from commandline successful"
else
echo "not ok 4 - decrypt commandline argument with password from commandline unsuccessful"
fi
DECRYPTED05="$(src/libexim-encrypt-dlfunc-decrypt-secretbox -p ${TEST_PASSWORD} --infile ${CIPHERTEXT_FILE02})"
if [ "${DECRYPTED05}" == "${TEST_CLEARTEXT}" ] ; then
echo "ok 5 - decrypt file contents with password from commandline successful"
else
echo "not ok 5 - decrypt file contents with password from commandline unsuccessful"
fi

View File

@ -1,24 +1,24 @@
#!/bin/bash #!/bin/bash
set -e
PATH=/sbin:/usr/sbin:$PATH PATH=/sbin:/usr/sbin:$PATH
# this script implements the TAP protocol (https://testanything.org)
echo 1..2
# copy to /tmp to keep call to exim under 256 chars (prevent problems on Ubuntu) # copy to /tmp to keep call to exim under 256 chars (prevent problems on Ubuntu)
install -t /tmp src/libexim-encrypt-dlfunc.so install -t /tmp src/libexim-encrypt-dlfunc.so
LIB=/tmp/libexim-encrypt-dlfunc.so LIB=/tmp/libexim-encrypt-dlfunc.so
CLEARTEXT="127.88.99.23" # keep short; see above CLEARTEXT="127.88.99.23" # keep short; see above
PASSWORD="`openssl rand -base64 32`" PASSWORD="$(openssl rand -base64 32)"
CIPHERTEXT=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_encrypt_password}{${PASSWORD}}{${CLEARTEXT}}}") CIPHERTEXT=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_encrypt_password}{${PASSWORD}}{${CLEARTEXT}}}")
DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_decrypt_password}{${PASSWORD}}{${CIPHERTEXT}}}") DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_decrypt_password}{${PASSWORD}}{${CIPHERTEXT}}}")
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
echo "secretbox test successful" echo "ok 1 - secretbox test successful"
else else
echo "secretbox test unsuccessful" echo "not ok 1 - secretbox test unsuccessful"
exit 127
fi fi
# { 0xb6, 0x01, 0x45, 0x20, 0x9f, 0x55, 0x06, 0x74, 0x29, 0x71, 0x7b, 0x5e, 0xa9, 0x68, 0x60, 0x5e, 0x81, 0x1a, 0x54, 0x6b, 0xc9, 0x80, 0x97, 0x78, 0x41, 0xc6, 0x20, 0xae, 0x66, 0x9f, 0xd9, 0x53 }; # { 0xb6, 0x01, 0x45, 0x20, 0x9f, 0x55, 0x06, 0x74, 0x29, 0x71, 0x7b, 0x5e, 0xa9, 0x68, 0x60, 0x5e, 0x81, 0x1a, 0x54, 0x6b, 0xc9, 0x80, 0x97, 0x78, 0x41, 0xc6, 0x20, 0xae, 0x66, 0x9f, 0xd9, 0x53 };
@ -30,8 +30,7 @@ CIPHERTEXT=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal}{${PK}}{${CLEART
DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal_open}{${SK}}{${PK}}{${CIPHERTEXT}}}") DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal_open}{${SK}}{${PK}}{${CIPHERTEXT}}}")
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
echo "sealed_box test successful" echo "ok 2 - sealed_box test successful"
else else
echo "sealed_box test unsuccessful" echo "ok 2 - sealed_box test unsuccessful"
exit 128
fi fi