Rework tests and AES CPU detection

This commit is contained in:
Travis Burtrum 2020-01-01 02:17:35 -05:00
parent 7540fa7d7c
commit fe7c7f982b
3 changed files with 65 additions and 35 deletions

View File

@ -27,19 +27,7 @@ ls -lah pegh.static.*
file pegh.static.* file pegh.static.*
ldd pegh.static.* || true ldd pegh.static.* || true
# libsodium only supports AES-256-GCM on certain CPUs that have hardware instructions for it export TEST_BINS="./pegh.static.openssl ./pegh.openssl ./pegh.static.libsodium-openssl ./pegh.libsodium-openssl ./pegh.static.libsodium ./pegh.libsodium"
# we can build them regardless, but we can't test them without that, pegh prints that right away
export TEST_BINS="./pegh.static.openssl ./pegh.openssl ./pegh.static.libsodium-openssl ./pegh.libsodium-openssl"
set +e
if ./pegh.static.libsodium -h 2>&1 >/dev/null | grep '^Error: libsodium'
then
echo "CPU does not have AES support so can't run libsodium version"
else
echo "CPU has AES support so can run libsodium version"
# we can test everything
export TEST_BINS="$TEST_BINS ./pegh.libsodium ./pegh.static.libsodium"
fi
set -e
# compile dynamically linked versions (with gcc) to openssl and libsodium, then test all 4 against each other # compile dynamically linked versions (with gcc) to openssl and libsodium, then test all 4 against each other
./test.sh ./test.sh

53
pegh.c
View File

@ -730,9 +730,10 @@ int check_version(uint8_t version, FILE *err) {
return 0; return 0;
} }
#ifdef PEGH_LIBSODIUM #ifdef PEGH_LIBSODIUM
else if (version == 0 && crypto_aead_aes256gcm_is_available() == 0) { /* check for AES encrypted but libsodium/hardware AES is not available */
/* AES encrypted but libsodium/hardware AES is not available */
#ifdef PEGH_OPENSSL #ifdef PEGH_OPENSSL
/* if we've already checked/set this, don't again */
else if (gcm_encrypt != gcm_encrypt_openssl && version == 0 && crypto_aead_aes256gcm_is_available() == 0) {
/* swap to OpenSSL AES which is always supported */ /* swap to OpenSSL AES which is always supported */
if(NULL != err) if(NULL != err)
fprintf(err, "Warning: libsodium does not support AES-256-GCM on this CPU, falling back to openssl version instead...\n"); fprintf(err, "Warning: libsodium does not support AES-256-GCM on this CPU, falling back to openssl version instead...\n");
@ -740,9 +741,10 @@ int check_version(uint8_t version, FILE *err) {
gcm_decrypt = gcm_decrypt_openssl; gcm_decrypt = gcm_decrypt_openssl;
CHUNK_SIZE_MAX_GCM = CHUNK_SIZE_MAX_OPENSSL_GCM; CHUNK_SIZE_MAX_GCM = CHUNK_SIZE_MAX_OPENSSL_GCM;
#else #else
else if (version == 0 && crypto_aead_aes256gcm_is_available() == 0) {
/* nothing we can do */ /* nothing we can do */
fprintf(stderr, "Error: libsodium does not support AES-256-GCM on this CPU, compile/use openssl version?\n"); fprintf(stderr, "Error: libsodium does not support AES-256-GCM on this CPU, compile/use openssl version?\n");
return 0; return 19;
#endif /* PEGH_OPENSSL */ #endif /* PEGH_OPENSSL */
} }
#endif /* PEGH_LIBSODIUM */ #endif /* PEGH_LIBSODIUM */
@ -795,6 +797,8 @@ int pegh_decrypt(char *password,
{ {
unsigned char salt[SALT_LEN] = {0}; unsigned char salt[SALT_LEN] = {0};
int version_exit_code;
size_t header_read, buffer_size; size_t header_read, buffer_size;
uint32_t N; uint32_t N;
@ -808,9 +812,9 @@ int pegh_decrypt(char *password,
return 0; return 0;
} }
version = salt[0]; version = salt[0];
if(1 != check_version(version, err)) { if(1 != (version_exit_code = check_version(version, err))) {
fprintf(stderr, "Error: decryption aborting, this file cannot be decrypted with this version/CPU\n"); fprintf(stderr, "Error: decryption aborting, this file cannot be decrypted with this version/CPU\n");
return 0; return version_exit_code;
} }
N = read_uint32_big_endian(salt+1); N = read_uint32_big_endian(salt+1);
r = salt[5]; r = salt[5];
@ -922,7 +926,7 @@ uint32_t next_highest_power_of_2(uint32_t v) {
return ++v; return ++v;
} }
/* returns 0 on success, 1 on openssl failure, 2 on other failure */ /* returns 0 on success, 1 on cryptography failure, 19 on "libsodium only and CPU does not support AES" error, 2 on other failure */
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int optind, decrypt = 0, append = 0, exit_code = 2, version = -1; int optind, decrypt = 0, append = 0, exit_code = 2, version = -1;
@ -933,6 +937,13 @@ int main(int argc, char **argv)
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 1;
}
#endif /* PEGH_LIBSODIUM */
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] == '-') {
@ -988,10 +999,27 @@ int main(int argc, char **argv)
err = NULL; err = NULL;
break; break;
case 'v': case 'v':
version = parse_byte_arg(++optind, argc, argv); if(++optind >= argc) {
fprintf(stderr, "Error: %s requires an argument\n", argv[optind - 1]);
return help(2);
}
if(strlen(argv[optind]) != 1 || (argv[optind][0] != '0' && argv[optind][0] != '1')) {
fprintf(stderr, "Error: unsupported file format version %s, we only support version 0 (AES-256-GCM) and 1 (Chacha20-Poly1305)\n", argv[optind]);
return help(2);
}
if(argv[optind][0] == '0') {
version = 0;
#ifdef PEGH_LIBSODIUM
if(1 != check_version(0, NULL)) {
return help(19);
}
#endif
} else {
version = 1;
}
break; break;
case 'V': case 'V':
fprintf(stderr, "pegh %s\nformat versions supported: 0\n", PEGH_VERSION); fprintf(stderr, "pegh %s\nformat versions supported: 0 (AES-256-GCM) and 1 (Chacha20-Poly1305)\n", PEGH_VERSION);
return 0; return 0;
case 'h': case 'h':
return help(0); return help(0);
@ -1030,13 +1058,6 @@ int main(int argc, char **argv)
return 0; return 0;
*/ */
#ifdef PEGH_LIBSODIUM
if (sodium_init() == -1) {
fprintf(stderr, "Error: libsodium could not be initialized, compile/use openssl version?\n");
return 2;
}
#endif /* PEGH_LIBSODIUM */
if(NULL != in_filename) { if(NULL != in_filename) {
in = fopen(in_filename, "rb"); in = fopen(in_filename, "rb");
if(!in) { if(!in) {
@ -1089,5 +1110,5 @@ int main(int argc, char **argv)
} }
/* to the OS, 0 means success, the above functions 1 means success */ /* to the OS, 0 means success, the above functions 1 means success */
return exit_code == 1 ? 0 : 1; return exit_code == 1 ? 0 : (exit_code == 0 ? 1 : exit_code);
} }

33
test.sh
View File

@ -35,28 +35,44 @@ echo "key: $key"
test () { test () {
bin="$1" bin="$1"
bin_decrypt="${2:-$bin}" shift
bin_decrypt="${1:-$bin}"
shift
echo "testing binaries bin: $bin bin_decrypt: $bin_decrypt" echo "testing binaries bin: $bin bin_decrypt: $bin_decrypt"
set +eu
if [ "$2" != "1" ]
then
# check both binaries through full pipe to see if it fails with an AES error
echo a | "$bin" "$@" "$key" | "$bin_decrypt" -d "$key" >/dev/null
# 19 is the special return code that means specifically libsodium-only and CPU doesn't support AES
[ ${PIPESTATUS[1]} -eq 19 -o ${PIPESTATUS[2]} -eq 19 ] && set -eu && echo "skipping this test because libsodium doesn't support AES on this CPU" && return 0
fi
set -eu
echo 'encrypting same data with same key should result in different ciphertext'
cmp <(echo a | "$bin" "$@" "$key") <(echo a | "$bin" "$@" "$key") && echo "random generation broken? same data and key resulted in same decryption so salt generation is broken and this is insecure" && exit 1 || true
echo 'encrypting then decrypting with the same key should succeed' echo 'encrypting then decrypting with the same key should succeed'
"$bin" -e "$key" < "$dummy_file" | "$bin_decrypt" -d "$key" | cmp - "$dummy_file" "$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 < "$dummy_file" | "$bin_decrypt" -d "$key" -m 2048 | cmp - "$dummy_file" "$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 "$dummy_file" | "$bin_decrypt" -d "$key-wrongkey" | cmp - "$dummy_file" && 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 "$dummy_file" >/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_decrypt" -d "$key" -N 2000000 -i "$dummy_file" >/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
echo 'bad decryption bytes are never output, file should be 0 bytes' echo 'bad decryption bytes are never output, file should be 0 bytes'
echo 'hopefully this doesnt make it to disk' | "$bin" "$key" | cat - <(echo -n a) | "$bin_decrypt" -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 be empty" && exit 1 [ -s bla.txt ] && echo "ERROR: bla.txt should be empty" && exit 1
set -e set -e
} }
@ -65,7 +81,12 @@ for bin in $TEST_BINS
do do
for bin_decrypt in $TEST_BINS for bin_decrypt in $TEST_BINS
do do
# test default versions
time test $bin $bin_decrypt time test $bin $bin_decrypt
# test aes
time test $bin $bin_decrypt -v 0
# test chacha
time test $bin $bin_decrypt -v 1
done done
done done