mirror of
https://gitlab.kit.edu/kit/scc/sys/mail/exim-encrypt-dlfunc.git
synced 2025-12-06 06:23:56 +01:00
Merge branch 'decryption_tools' into 'main'
Add decryption tools Closes #1 See merge request mail/exim-encrypt-dlfunc!4
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/build
|
||||
debian/*.ex
|
||||
|
||||
|
||||
@ -21,8 +21,10 @@ stages:
|
||||
- cd ..
|
||||
artifacts:
|
||||
paths:
|
||||
- build/src/generate_encryption_keys
|
||||
- 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:
|
||||
stage: debian-package
|
||||
@ -37,31 +39,23 @@ stages:
|
||||
- ./*.changes
|
||||
- ./*.buildinfo
|
||||
|
||||
build:buster:
|
||||
extends:
|
||||
- .build
|
||||
- .image-buster
|
||||
needs: []
|
||||
|
||||
build:bullseye:
|
||||
extends:
|
||||
- .image-bullseye
|
||||
- .build
|
||||
needs: []
|
||||
needs: [ ]
|
||||
|
||||
build:focal:
|
||||
extends:
|
||||
- .image-focal
|
||||
- .build
|
||||
needs: []
|
||||
needs: [ ]
|
||||
|
||||
debian-package:buster:
|
||||
build:buster:
|
||||
extends:
|
||||
- .build
|
||||
- .image-buster
|
||||
- .debian-package
|
||||
dependencies:
|
||||
- build:buster
|
||||
needs: ["build:buster"]
|
||||
needs: [ ]
|
||||
|
||||
debian-package:bullseye:
|
||||
extends:
|
||||
@ -69,7 +63,7 @@ debian-package:bullseye:
|
||||
- .debian-package
|
||||
dependencies:
|
||||
- build:bullseye
|
||||
needs: ["build:bullseye"]
|
||||
needs: [ "build:bullseye" ]
|
||||
|
||||
debian-package:focal:
|
||||
extends:
|
||||
@ -77,4 +71,12 @@ debian-package:focal:
|
||||
- .debian-package
|
||||
dependencies:
|
||||
- build:focal
|
||||
needs: ["build:focal"]
|
||||
needs: [ "build:focal" ]
|
||||
|
||||
debian-package:buster:
|
||||
extends:
|
||||
- .image-buster
|
||||
- .debian-package
|
||||
dependencies:
|
||||
- build:buster
|
||||
needs: [ "build:buster" ]
|
||||
|
||||
76
README.md
76
README.md
@ -44,7 +44,10 @@ meson compile -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
|
||||
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
|
||||
[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.
|
||||
|
||||
Try
|
||||
|
||||
```shell
|
||||
exim4 --version | egrep -i --color 'Expand_dlfunc|Content_Scanning'
|
||||
```
|
||||
|
||||
for a preliminary test.
|
||||
|
||||
## Usage
|
||||
|
||||
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`
|
||||
|
||||
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.
|
||||
|
||||
```shell
|
||||
$ ./generate_encryption_keys
|
||||
$ libexim-encrypt-dlfunc-genkeys
|
||||
=== Creating cryptobox key pair ===
|
||||
Wrote »cryptobox_recipient_pk_exim.conf«
|
||||
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
|
||||
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} \
|
||||
{ktp1OEEItrgvSfpVTtu+ybyNjzuuN8OzCdfrGAJt4j8=} \
|
||||
{$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} \
|
||||
{Insert your password here} \
|
||||
{$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.
|
||||
|
||||
### 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.
|
||||
```
|
||||
@ -3,7 +3,7 @@
|
||||
## Prerequisites
|
||||
|
||||
* [buildah](https://buildah.io/)
|
||||
* {podman](https://podman.io/)
|
||||
* [podman](https://podman.io/)
|
||||
|
||||
## Build and upload
|
||||
|
||||
|
||||
@ -61,11 +61,14 @@ for i in "${images[@]}"; do
|
||||
openssl; \
|
||||
DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install -y \
|
||||
debhelper \
|
||||
dh-make \
|
||||
devscripts \
|
||||
git-buildpackage \
|
||||
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/*;'
|
||||
buildah run "$ctr" /bin/sh -c \
|
||||
'pip3 install meson ninja; \
|
||||
|
||||
89
src/common.c
Normal file
89
src/common.c
Normal 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
14
src/common.h
Normal 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
|
||||
@ -20,7 +20,7 @@ char *string2hex(unsigned char *input, size_t length) {
|
||||
* 1. Add this code to the first “breakpoint”:
|
||||
* log_write(0, LOG_MAIN, "pid: %d", getpid()); int busywait = 0; while (busywait == 0) {}
|
||||
* 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”
|
||||
* 5. Prepare breakpoints, watches, etc. Set busywait to 1 and continue.
|
||||
*/
|
||||
203
src/libexim-encrypt-dlfunc-decrypt-sealedbox.c
Normal file
203
src/libexim-encrypt-dlfunc-decrypt-sealedbox.c
Normal 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);
|
||||
}
|
||||
}
|
||||
161
src/libexim-encrypt-dlfunc-decrypt-secretbox.c
Normal file
161
src/libexim-encrypt-dlfunc-decrypt-secretbox.c
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,14 @@
|
||||
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 ],
|
||||
install: true)
|
||||
|
||||
@ -8,7 +16,14 @@ shared_library('exim-encrypt-dlfunc', 'libexim-encrypt-dlfunc.c',
|
||||
dependencies : [ sodium_deps ],
|
||||
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('simple test', simple_exim_test)
|
||||
test('decrypt-secretbox',
|
||||
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')
|
||||
46
src/test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh
Executable file
46
src/test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh
Executable 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
|
||||
57
src/test_libexim-encrypt-dlfunc-decrypt-secretbox.sh
Executable file
57
src/test_libexim-encrypt-dlfunc-decrypt-secretbox.sh
Executable 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
|
||||
@ -1,24 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
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)
|
||||
install -t /tmp src/libexim-encrypt-dlfunc.so
|
||||
|
||||
LIB=/tmp/libexim-encrypt-dlfunc.so
|
||||
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}}}")
|
||||
DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_decrypt_password}{${PASSWORD}}{${CIPHERTEXT}}}")
|
||||
|
||||
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
|
||||
echo "secretbox test successful"
|
||||
echo "ok 1 - secretbox test successful"
|
||||
else
|
||||
echo "secretbox test unsuccessful"
|
||||
exit 127
|
||||
echo "not ok 1 - secretbox test unsuccessful"
|
||||
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 };
|
||||
@ -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}}}")
|
||||
|
||||
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
|
||||
echo "sealed_box test successful"
|
||||
echo "ok 2 - sealed_box test successful"
|
||||
else
|
||||
echo "sealed_box test unsuccessful"
|
||||
exit 128
|
||||
echo "ok 2 - sealed_box test unsuccessful"
|
||||
fi
|
||||
Reference in New Issue
Block a user