Add support for compiling against openssl OR libsodium, and tests for both against each other

This commit is contained in:
Travis Burtrum 2019-12-29 00:47:36 -05:00
parent 8a48f437d9
commit 643cd327f4
4 changed files with 280 additions and 74 deletions

4
.gitignore vendored
View File

@ -1,3 +1,7 @@
pegh pegh
pegh.exe pegh.exe
pegh.libsodium
pegh.openssl
bla.txt bla.txt
*.kate-swp

View File

@ -5,9 +5,21 @@
CFLAGS += -Wall -Wextra -Werror -std=c89 -pedantic \ CFLAGS += -Wall -Wextra -Werror -std=c89 -pedantic \
-Wstrict-prototypes -Wold-style-definition -Wconversion \ -Wstrict-prototypes -Wold-style-definition -Wconversion \
-Wno-missing-prototypes -Wno-missing-noreturn \ -Wno-missing-prototypes -Wno-missing-noreturn \
-O3 -O3
ifdef PEGH_OPENSSL
CFLAGS += -DPEGH_OPENSSL
LDFLAGS += -lcrypto LDFLAGS += -lcrypto
else
ifdef PEGH_LIBSODIUM
CFLAGS += -DPEGH_LIBSODIUM
LDFLAGS += -lsodium
else
CFLAGS += -DPEGH_OPENSSL
LDFLAGS += -lcrypto
endif
endif
all : pegh all : pegh

289
pegh.c
View File

@ -19,15 +19,11 @@
/* compile with: cc pegh.c -lcrypto -O3 -o pegh */ /* compile with: cc pegh.c -lcrypto -O3 -o pegh */
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <limits.h> #include <limits.h>
#include <errno.h>
/* /*
* tweak default scrypt hardness params here * tweak default scrypt hardness params here
@ -38,10 +34,10 @@
#define SCRYPT_N 32768 #define SCRYPT_N 32768
#define SCRYPT_R 8 #define SCRYPT_R 8
#define SCRYPT_P 1 #define SCRYPT_P 1
#define SCRYPT_MAX_MEM_MB 64 #define SCRYPT_MAX_MEM 1024 * 1024 * 64 /* 64 megabytes */
/* tweak buffer sizes here, memory use will be twice this */ /* tweak buffer sizes here, memory use will be twice this */
#define BUFFER_SIZE_MB 16 #define BUFFER_SIZE_MB 32
/* /*
* pegh file format, numbers are inclusive 0-based byte array indices * pegh file format, numbers are inclusive 0-based byte array indices
@ -80,6 +76,21 @@
#define IV_LEN 12 #define IV_LEN 12
#define GCM_TAG_LEN 16 #define GCM_TAG_LEN 16
/* default of OpenSSL for now... */
#if !defined(PEGH_OPENSSL) && !defined(PEGH_LIBSODIUM)
#define PEGH_OPENSSL 1
#endif
#ifdef PEGH_OPENSSL
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/rand.h>
/* this is because we read up to buffer_size at once, and then send that value to openssl which uses int instead of size_t, limit of 2gb */
static const size_t CHUNK_SIZE_MAX = INT_MAX;
/* /*
* returns 1 on success, 0 on failure * returns 1 on success, 0 on failure
* *
@ -93,7 +104,7 @@
* ciphertext must have the capacity of at least plaintext_len * ciphertext must have the capacity of at least plaintext_len
* tag must have the capacity of at least GCM_TAG_LEN * tag must have the capacity of at least GCM_TAG_LEN
*/ */
int gcm_encrypt(const unsigned char *plaintext, const int plaintext_len, int gcm_encrypt(const unsigned char *plaintext, const size_t plaintext_len,
const unsigned char *key, const unsigned char *key,
const unsigned char *iv, const unsigned char *iv,
unsigned char *ciphertext, unsigned char *ciphertext,
@ -125,7 +136,7 @@ int gcm_encrypt(const unsigned char *plaintext, const int plaintext_len,
* Provide the message to be encrypted, and obtain the encrypted output. * Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can be called multiple times if necessary * EVP_EncryptUpdate can be called multiple times if necessary
*/ */
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &ciphertext_written, plaintext, plaintext_len)) if(1 != EVP_EncryptUpdate(ctx, ciphertext, &ciphertext_written, plaintext, (int) plaintext_len))
break; break;
/* if this isn't true, GCM is broken, we probably don't need to check... /* if this isn't true, GCM is broken, we probably don't need to check...
@ -167,7 +178,7 @@ int gcm_encrypt(const unsigned char *plaintext, const int plaintext_len,
* these will be written into: * these will be written into:
* plaintext must have the capacity of at least ciphertext_len * plaintext must have the capacity of at least ciphertext_len
*/ */
int gcm_decrypt(const unsigned char *ciphertext, const int ciphertext_len, int gcm_decrypt(const unsigned char *ciphertext, const size_t ciphertext_len,
const unsigned char *key, const unsigned char *key,
const unsigned char *iv, const unsigned char *iv,
unsigned char *tag, unsigned char *tag,
@ -199,7 +210,7 @@ int gcm_decrypt(const unsigned char *ciphertext, const int ciphertext_len,
* Provide the message to be decrypted, and obtain the plaintext output. * Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary * EVP_DecryptUpdate can be called multiple times if necessary
*/ */
if(!EVP_DecryptUpdate(ctx, plaintext, &plaintext_written, ciphertext, ciphertext_len)) if(!EVP_DecryptUpdate(ctx, plaintext, &plaintext_written, ciphertext, (int) ciphertext_len))
break; break;
/* if this isn't true, GCM is broken, we probably don't need to check... /* if this isn't true, GCM is broken, we probably don't need to check...
@ -228,6 +239,146 @@ int gcm_decrypt(const unsigned char *ciphertext, const int ciphertext_len,
return ret; return ret;
} }
/* returns 1 on success, 0 on error */
int scrypt_derive_key(char *password, size_t password_len,
uint32_t scrypt_max_mem, uint32_t N,
uint8_t r, uint8_t p, unsigned char *salt, unsigned char *key, FILE *err) {
/* derive key using salt, password, and scrypt parameters */
if (EVP_PBE_scrypt(
password, password_len,
salt, SALT_LEN,
(uint64_t) N, (uint64_t) r, (uint64_t) p,
(uint64_t) scrypt_max_mem,
key, KEY_LEN
) <= 0) {
if(NULL != err) {
fprintf(err, "scrypt key derivation error\n");
ERR_print_errors_fp(err);
}
return 0;
}
return 1;
}
/* returns 1 on success, 0 on error */
int random_salt(unsigned char *salt) {
return RAND_bytes(salt, SALT_LEN) <= 0 ? 0 : 1;
}
void wipe_memory(void * const ptr, const size_t len) {
OPENSSL_cleanse(ptr, len);
}
#endif
#ifdef PEGH_LIBSODIUM
#include <sodium.h>
/* unlike openssl, libsodium uses proper types, so we can go all the way up to the "aes-gcm-256 is still secure" limit of around 32gb */
static const size_t CHUNK_SIZE_MAX = 1024UL * 1024 * 1024 * 32;
/*
* returns 1 on success, 0 on failure
*
* these will be read from:
* plaintext
* plaintext_len
* key must be length KEY_LEN
* iv must be length IV_LEN
*
* these will be written into:
* ciphertext must have the capacity of at least plaintext_len
* tag must have the capacity of at least GCM_TAG_LEN
*/
int gcm_encrypt(const unsigned char *plaintext, const size_t plaintext_len,
const unsigned char *key,
const unsigned char *iv,
unsigned char *ciphertext,
unsigned char *tag
)
{
crypto_aead_aes256gcm_encrypt_detached(ciphertext,
tag, NULL,
plaintext, plaintext_len,
NULL, 0,
NULL, iv, key);
return 1;
}
/*
* returns 1 on success, 0 on failure
*
* these will be read from:
* ciphertext
* ciphertext_len
* key must be length KEY_LEN
* iv must be length IV_LEN
* tag must be length GCM_TAG_LEN
*
* these will be written into:
* plaintext must have the capacity of at least ciphertext_len
*/
int gcm_decrypt(const unsigned char *ciphertext, const size_t ciphertext_len,
const unsigned char *key,
const unsigned char *iv,
unsigned char *tag,
unsigned char *plaintext
)
{
return crypto_aead_aes256gcm_decrypt_detached(plaintext,
NULL,
ciphertext, (size_t) ciphertext_len,
tag,
NULL, 0,
iv, key) != 0 ? 0 : 1;
}
/* returns 1 on success, 0 on error */
int scrypt_derive_key(char *password, size_t password_len,
uint32_t scrypt_max_mem, uint32_t N,
uint8_t r, uint8_t p, unsigned char *salt, unsigned char *key, FILE *err) {
size_t needed_memory;
/* derive key using salt, password, and scrypt parameters */
/* this is how crypto_pwhash_scryptsalsa208sha256_ll calculates the memory needed, so do it here first and check */
needed_memory = (size_t) 128 * r * p;
needed_memory += (size_t) 128 * r * (size_t) N;
needed_memory += (size_t) 256 * r + 64;
if (needed_memory > scrypt_max_mem) {
if(NULL != err) {
/* +1 is to round up here and avoid math.h and ceil()... */
fprintf(err, "scrypt key derivation error, needed memory %lu mb, allowed memory %d mb, increase -m\n", (needed_memory / 1024 / 1024) + 1, scrypt_max_mem / 1024 / 1024);
}
return 0;
}
if (crypto_pwhash_scryptsalsa208sha256_ll(
(const uint8_t *) password, password_len,
salt, SALT_LEN,
(uint64_t) N, (uint32_t) r, (uint32_t) p,
key, KEY_LEN
) < 0) {
if(NULL != err) {
fprintf(err, "scrypt key derivation error\n");
}
return 0;
}
return 1;
}
/* returns 1 on success, 0 on error */
int random_salt(unsigned char *salt) {
randombytes_buf(salt, SALT_LEN);
return 1;
}
void wipe_memory(void * const ptr, const size_t len) {
sodium_memzero(ptr, len);
}
#endif
/* returns 1 on success, 0 on failure */ /* returns 1 on success, 0 on failure */
int iv_increment_forbid_zero(unsigned char *n, const size_t nlen, FILE *err) int iv_increment_forbid_zero(unsigned char *n, const size_t nlen, FILE *err)
{ {
@ -256,7 +407,7 @@ int gcm_encrypt_stream(const unsigned char *key, unsigned char *iv, size_t buffe
while ((plaintext_read = fread(plaintext, 1, buffer_size, in)) > 0) { while ((plaintext_read = fread(plaintext, 1, buffer_size, in)) > 0) {
if(1 != gcm_encrypt(plaintext, (int) plaintext_read, key, iv, ciphertext, ciphertext + plaintext_read)) if(1 != gcm_encrypt(plaintext, plaintext_read, key, iv, ciphertext, ciphertext + plaintext_read))
return 0; return 0;
if(1 != iv_increment_forbid_zero(iv, IV_LEN, err)) if(1 != iv_increment_forbid_zero(iv, IV_LEN, err))
@ -286,7 +437,7 @@ int gcm_decrypt_stream(const unsigned char *key, unsigned char *iv, size_t buffe
ciphertext_read -= GCM_TAG_LEN; ciphertext_read -= GCM_TAG_LEN;
if(1 != gcm_decrypt(ciphertext, (int) ciphertext_read, key, iv, ciphertext + ciphertext_read, plaintext)) if(1 != gcm_decrypt(ciphertext, ciphertext_read, key, iv, ciphertext + ciphertext_read, plaintext))
return 0; return 0;
if(1 != iv_increment_forbid_zero(iv, IV_LEN, err)) if(1 != iv_increment_forbid_zero(iv, IV_LEN, err))
@ -321,10 +472,15 @@ int gcm_stream(const unsigned char *key, size_t buffer_size,
int exit_code = 0; int exit_code = 0;
if(buffer_size > INT_MAX) { if(buffer_size > CHUNK_SIZE_MAX) {
/* this is because we read up to buffer_size at once, and then send that value to openssl which uses int instead of size_t */ if(NULL != err) {
if(NULL != err) #ifdef PEGH_OPENSSL
fprintf(err, "due to openssl API buffer_size can at most be %d\n", INT_MAX); fprintf(err, "due to openssl API limitation, buffer_size can at most be %ld\n", CHUNK_SIZE_MAX);
#endif
#ifdef PEGH_LIBSODIUM
fprintf(err, "due to AES-256-GCM security constraints, buffer_size can at most be %ld\n", CHUNK_SIZE_MAX);
#endif
}
return 0; return 0;
} }
@ -349,35 +505,16 @@ int gcm_stream(const unsigned char *key, size_t buffer_size,
free(ciphertext); free(ciphertext);
if(NULL != err && exit_code != 1) { if(NULL != err && exit_code != 1) {
#ifdef PEGH_OPENSSL
/* print openssl errors */ /* print openssl errors */
ERR_print_errors_fp(err); ERR_print_errors_fp(err);
#endif
fprintf(err, "%scryption failed\n", decrypt ? "de" : "en"); fprintf(err, "%scryption failed\n", decrypt ? "de" : "en");
} }
return exit_code; return exit_code;
} }
/* returns 1 on success, 0 on error */
int scrypt_derive_key(char *password,
uint32_t scrypt_max_mem_mb, uint32_t N,
uint8_t r, uint8_t p, unsigned char *salt, unsigned char *key, FILE *err) {
/* derive key using salt, password, and scrypt parameters */
if (EVP_PBE_scrypt(
password, strlen(password),
salt, SALT_LEN,
(uint64_t) N, (uint64_t) r, (uint64_t) p,
(uint64_t) scrypt_max_mem_mb * 1024 * 1024,
key, KEY_LEN
) <= 0) {
if(NULL != err) {
fprintf(err, "scrypt key derivation error\n");
ERR_print_errors_fp(err);
}
return 0;
}
return 1;
}
/* buf must be at least 4 bytes */ /* buf must be at least 4 bytes */
uint32_t read_uint32_big_endian(const unsigned char *buf) { uint32_t read_uint32_big_endian(const unsigned char *buf) {
return (uint32_t) ((buf[0] & 0xFF) << 24) return (uint32_t) ((buf[0] & 0xFF) << 24)
@ -394,13 +531,34 @@ void write_uint32_big_endian(uint32_t val, unsigned char *buf) {
buf[3] = val & 0xFF; buf[3] = val & 0xFF;
} }
/* returns 1 on success, 0 on failure */
int scrypt_derive_key_gcm_stream(char *password,
uint32_t scrypt_max_mem, size_t buffer_size,
FILE *in, FILE *out, FILE *err,
uint32_t N, uint8_t r, uint8_t p, unsigned char *salt, int decrypt) {
unsigned char key[KEY_LEN] = {0};
int ret;
size_t password_len;
password_len = strlen(password);
ret = scrypt_derive_key(password, password_len, scrypt_max_mem, N, r, p, salt, key, err);
wipe_memory(password, password_len);
if(ret == 1)
ret = gcm_stream(key, buffer_size, decrypt, in, out, err);
wipe_memory(key, KEY_LEN);
return ret;
}
/* returns 1 on success, 0 on failure */ /* returns 1 on success, 0 on failure */
int pegh_encrypt(char *password, int pegh_encrypt(char *password,
uint32_t scrypt_max_mem_mb, size_t buffer_size, uint32_t scrypt_max_mem, size_t buffer_size,
FILE *in, FILE *out, FILE *err, FILE *in, FILE *out, FILE *err,
uint32_t N, uint8_t r, uint8_t p) uint32_t N, uint8_t r, uint8_t p)
{ {
unsigned char key[KEY_LEN] = {0}, salt[SALT_LEN] = {0}; unsigned char salt[SALT_LEN] = {0};
/* first write the version and parameters */ /* first write the version and parameters */
salt[0] = 0; salt[0] = 0;
@ -411,27 +569,26 @@ int pegh_encrypt(char *password,
fwrite(salt, 1, PRE_SALT_LEN, out); fwrite(salt, 1, PRE_SALT_LEN, out);
/* generate random salt, then write it out */ /* generate random salt, then write it out */
if (RAND_bytes(salt, SALT_LEN) <= 0) { if (random_salt(salt) != 1) {
if(NULL != err) { if(NULL != err) {
fprintf(err, "random salt generation error\n"); fprintf(err, "random salt generation error\n");
#ifdef PEGH_OPENSSL
ERR_print_errors_fp(err); ERR_print_errors_fp(err);
#endif
} }
return 0; return 0;
} }
fwrite(salt, 1, SALT_LEN, out); fwrite(salt, 1, SALT_LEN, out);
if(1 != scrypt_derive_key(password, scrypt_max_mem_mb, N, r, p, salt, key, err)) return scrypt_derive_key_gcm_stream(password, scrypt_max_mem, buffer_size, in, out, err, N, r, p, salt, 0);
return 0;
return gcm_stream(key, buffer_size, 0, in, out, err);
} }
/* returns 1 on success, 0 on failure */ /* returns 1 on success, 0 on failure */
int pegh_decrypt(char *password, int pegh_decrypt(char *password,
uint32_t scrypt_max_mem_mb, size_t max_buffer_size, uint32_t scrypt_max_mem, size_t max_buffer_size,
FILE *in, FILE *out, FILE *err) FILE *in, FILE *out, FILE *err)
{ {
unsigned char key[KEY_LEN] = {0}, salt[SALT_LEN] = {0}; unsigned char salt[SALT_LEN] = {0};
size_t header_read, buffer_size; size_t header_read, buffer_size;
@ -468,10 +625,7 @@ int pegh_decrypt(char *password,
return 0; return 0;
} }
if(1 != scrypt_derive_key(password, scrypt_max_mem_mb, N, r, p, salt, key, err)) return scrypt_derive_key_gcm_stream(password, scrypt_max_mem, buffer_size, in, out, err, N, r, p, salt, 1);
return 0;
return gcm_stream(key, buffer_size, 1, in, out, err);
} }
int help(int exit_code) { int help(int exit_code) {
@ -492,11 +646,11 @@ usage: pegh [options...] password\n\
fprintf(stderr, "\ fprintf(stderr, "\
only allocated after scrypt is finished so max usage will be\n\ only allocated after scrypt is finished so max usage will be\n\
the highest of these only, not both combined,\n\ the highest of these only, not both combined,\n\
max: %d, default: %d\n\ max: %ld, default: %d\n\
-m <max_mb> maximum megabytes of ram to use when deriving key from password\n\ -m <max_mb> maximum megabytes of ram to use when deriving key from password\n\
with scrypt, applies for encryption AND decryption, must\n\ with scrypt, applies for encryption AND decryption, must\n\
almost linearly scale with -N, if too low operation will fail,\n\ almost linearly scale with -N, if too low operation will fail,\n\
default: %d\n", INT_MAX / 1024 / 1024, BUFFER_SIZE_MB, SCRYPT_MAX_MEM_MB); default: %d\n", CHUNK_SIZE_MAX / 1024 / 1024, BUFFER_SIZE_MB, SCRYPT_MAX_MEM / 1024 / 1024);
fprintf(stderr, "\ fprintf(stderr, "\
-N <num> scrypt parameter N, only applies for encryption, default %d\n\ -N <num> scrypt parameter N, only applies for encryption, default %d\n\
this is rounded up to the next highest power of 2\n\ this is rounded up to the next highest power of 2\n\
@ -567,12 +721,23 @@ int main(int argc, char **argv)
{ {
int optind, decrypt = 0, append = 0, exit_code = 2; int optind, decrypt = 0, append = 0, exit_code = 2;
char *password = NULL; char *password = NULL;
uint32_t N = SCRYPT_N, scrypt_max_mem_mb = SCRYPT_MAX_MEM_MB, buffer_size = BUFFER_SIZE_MB * 1024 * 1024, scale = 1; uint32_t N = SCRYPT_N, scrypt_max_mem = SCRYPT_MAX_MEM, buffer_size = BUFFER_SIZE_MB * 1024 * 1024, scale = 1;
uint8_t r = SCRYPT_R, p = SCRYPT_P; uint8_t r = SCRYPT_R, p = SCRYPT_P;
FILE *in = stdin, *out = stdout, *err = stderr; FILE *in = stdin, *out = stdout, *err = stderr;
char *in_filename = NULL, *out_filename = NULL; char *in_filename = NULL, *out_filename = NULL;
#ifdef PEGH_LIBSODIUM
if (sodium_init() == -1) {
fprintf(stderr, "Error: libsodium could not be initialized, compile/use openssl version?\n");
return 2;
}
if (crypto_aead_aes256gcm_is_available() == 0) {
fprintf(stderr, "Error: libsodium does not support AES-256-GCM on this CPU, compile/use openssl version?\n");
return 2;
}
#endif
for (optind = 1; optind < argc; ++optind) { for (optind = 1; optind < argc; ++optind) {
if(strlen(argv[optind]) == 2 && argv[optind][0] == '-') { if(strlen(argv[optind]) == 2 && argv[optind][0] == '-') {
@ -608,13 +773,13 @@ int main(int argc, char **argv)
break; break;
case 'c': case 'c':
buffer_size = parse_int_arg(++optind, argc, argv) * 1024 * 1024; buffer_size = parse_int_arg(++optind, argc, argv) * 1024 * 1024;
if(buffer_size > INT_MAX) { if(buffer_size > CHUNK_SIZE_MAX) {
fprintf(stderr, "Error: %s chunk size cannot exceed %d megabytes\n", argv[optind - 1], INT_MAX / 1024 / 1024); fprintf(stderr, "Error: %s chunk size cannot exceed %ld megabytes\n", argv[optind - 1], CHUNK_SIZE_MAX / 1024 / 1024);
return help(2); return help(2);
} }
break; break;
case 'm': case 'm':
scrypt_max_mem_mb = parse_int_arg(++optind, argc, argv); scrypt_max_mem = parse_int_arg(++optind, argc, argv) * 1024 * 1024;
break; break;
case 'N': case 'N':
N = next_highest_power_of_2(parse_int_arg(++optind, argc, argv)); N = next_highest_power_of_2(parse_int_arg(++optind, argc, argv));
@ -663,11 +828,11 @@ int main(int argc, char **argv)
/* apply scale */ /* apply scale */
N *= scale; N *= scale;
scrypt_max_mem_mb *= scale; scrypt_max_mem *= scale;
/* /*
fprintf (stderr, "decrypt = %d, key = %s, scrypt_max_mem_mb = %d, N = %d, r = %d, p = %d, scale = %d\n", fprintf (stderr, "decrypt = %d, key = %s, scrypt_max_mem = %d, N = %d, r = %d, p = %d, scale = %d\n",
decrypt, password, scrypt_max_mem_mb, N, r, p, scale); decrypt, password, scrypt_max_mem, N, r, p, scale);
return 0; return 0;
*/ */
@ -689,9 +854,9 @@ int main(int argc, char **argv)
} }
if(decrypt) if(decrypt)
exit_code = pegh_decrypt(password, scrypt_max_mem_mb, buffer_size, in, out, err); exit_code = pegh_decrypt(password, scrypt_max_mem, buffer_size, in, out, err);
else else
exit_code = pegh_encrypt(password, scrypt_max_mem_mb, buffer_size, in, out, err, N, r, p); exit_code = pegh_encrypt(password, scrypt_max_mem, buffer_size, in, out, err, N, r, p);
if(NULL != in_filename) if(NULL != in_filename)
fclose(in); fclose(in);

47
test.sh
View File

@ -1,45 +1,70 @@
#!/bin/bash #!/bin/bash
export dummy_file="$1"
shift
export dummy_mb="$1"
[ "$dummy_file" = "" ] && export dummy_file='/dev/shm/randombytes'
[ "$dummy_mb" = "" ] && export dummy_mb='100'
set -euo pipefail set -euo pipefail
# try different size files to encrypt/decrypt # try different size files to encrypt/decrypt
[ -e /dev/shm/randombytes ] || dd if=/dev/urandom bs=1M count=100 of=/dev/shm/randombytes [ -e "$dummy_file" ] || dd if=/dev/urandom bs=1M "count=$dummy_mb" of="$dummy_file"
# try make if it's installed, otherwise fall back to cc # try make if it's installed, otherwise fall back to cc
make || cc pegh.c -lcrypto -O3 -o pegh bins="./pegh.openssl ./pegh.libsodium"
#cargo build --release #bins="./pegh.libsodium ./pegh.openssl"
rm -f pegh $bins
export key="$(openssl rand -base64 20)" # compile against openssl
make PEGH_OPENSSL=1 || cc pegh.c -DPEGH_OPENSSL -lcrypto -O3 -o pegh
mv pegh pegh.openssl
# compile against libsodium
make PEGH_LIBSODIUM=1 || cc pegh.c -DPEGH_LIBSODIUM -lsodium -O3 -o pegh
mv pegh pegh.libsodium
export key="$(< /dev/urandom tr -dc 'a-z0-9' | head -c12)"
echo "key: $key" echo "key: $key"
test () { test () {
bin="$1" bin="$1"
bin_decrypt="${2:-$bin}"
echo "testing bins: $bin bin_decrypt: $bin_decrypt"
echo 'encrypting then decrypting with the same key should succeed' echo 'encrypting then decrypting with the same key should succeed'
"$bin" -e "$key" < /dev/shm/randombytes | "$bin" -d "$key" | cmp - /dev/shm/randombytes "$bin" -e "$key" < "$dummy_file" | "$bin_decrypt" -d "$key" | cmp - "$dummy_file"
echo 'test with -s 32 requiring 2gb of ram should succeed' echo 'test with -s 32 requiring 2gb of ram should succeed'
# can send -s 32 or -m 2048 to decrypt command with identical effect # can send -s 32 or -m 2048 to decrypt command with identical effect
"$bin" -e "$key" -s 32 < /dev/shm/randombytes | "$bin" -d "$key" -m 2048 | cmp - /dev/shm/randombytes "$bin" -e "$key" -s 32 < "$dummy_file" | "$bin_decrypt" -d "$key" -m 2048 | cmp - "$dummy_file"
set +e set +e
# these should fail # these should fail
echo 'encrypting with one key and decrypting with another should fail' echo 'encrypting with one key and decrypting with another should fail'
"$bin" -e "$key" -i /dev/shm/randombytes | "$bin" -d "$key-wrongkey" | cmp - /dev/shm/randombytes && echo "ERROR: appending -wrongkey to key somehow still worked" && exit 1 "$bin" -e "$key" -i "$dummy_file" | "$bin_decrypt" -d "$key-wrongkey" | cmp - "$dummy_file" && echo "ERROR: appending -wrongkey to key somehow still worked" && exit 1
echo 'large values of N without enough memory should fail' echo 'large values of N without enough memory should fail'
"$bin" -e "$key" -N 2000000 -i /dev/shm/randombytes >/dev/null && echo "ERROR: N of 2 million without extra memory worked" && exit 1 "$bin" -e "$key" -N 2000000 -i "$dummy_file" >/dev/null && echo "ERROR: N of 2 million without extra memory worked" && exit 1
"$bin" -d "$key" -N 2000000 -i /dev/shm/randombytes >/dev/null && echo "ERROR: N of 2 million without extra memory worked" && exit 1 "$bin_decrypt" -d "$key" -N 2000000 -i "$dummy_file" >/dev/null && echo "ERROR: N of 2 million without extra memory worked" && exit 1
# todo: can we also make this the case for stdout? needs some buffering... # todo: can we also make this the case for stdout? needs some buffering...
echo 'bad decryption should result in output file being deleted' echo 'bad decryption should result in output file being deleted'
echo 'hopefully this doesnt make it to disk' | "$bin" "$key" | cat - <(echo -n a) | "$bin" -d "$key" -o bla.txt && exit 1 echo 'hopefully this doesnt make it to disk' | "$bin" "$key" | cat - <(echo -n a) | "$bin_decrypt" -d "$key" -o bla.txt && exit 1
[ -s bla.txt ] && echo "ERROR: bla.txt should not exist" && exit 1 [ -s bla.txt ] && echo "ERROR: bla.txt should not exist" && exit 1
set -e set -e
} }
time test ./pegh for bin in $bins
do
for bin_decrypt in $bins
do
time test $bin $bin_decrypt
done
done
echo "successful test run!" echo "successful test run!"