diff --git a/.ci/build.sh b/.ci/build.sh index 70d7d4f..d2af8c9 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -14,6 +14,8 @@ make clean all PEGH_LIBSODIUM=1 CC=clang LDFLAGS="-static -lsodium" || clang peg mv pegh pegh.static.libsodium make clean all PEGH_OPENSSL=1 CC=clang LDFLAGS="-static -lcrypto" || clang pegh.c -DPEGH_OPENSSL -static -lcrypto -O3 -o pegh mv pegh pegh.static.openssl +make clean all PEGH_LIBSODIUM=1 PEGH_OPENSSL=1 CC=clang LDFLAGS="-static -lcrypto" || clang pegh.c -DPEGH_LIBSODIUM -DPEGH_OPENSSL -static -lsodium -lcrypto -O3 -o pegh +mv pegh pegh.static.libsodium-universal-aes ls -lah pegh.static.* @@ -31,11 +33,11 @@ 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" # no aes support - export TEST_BINS="./pegh.static.openssl ./pegh.openssl" + export TEST_BINS="./pegh.static.openssl ./pegh.openssl ./pegh.static.libsodium-universal-aes ./pegh.libsodium-universal-aes" else echo "CPU has AES support so can run libsodium version" # we can test everything - export TEST_BINS="./pegh.static.openssl ./pegh.openssl ./pegh.libsodium ./pegh.static.libsodium" + export TEST_BINS="./pegh.static.openssl ./pegh.openssl ./pegh.static.libsodium-universal-aes ./pegh.libsodium-universal-aes ./pegh.libsodium ./pegh.static.libsodium" fi set -e diff --git a/.ci/docker_build.sh b/.ci/docker_build.sh index 88e0ca6..e3e42d4 100755 --- a/.ci/docker_build.sh +++ b/.ci/docker_build.sh @@ -14,6 +14,7 @@ docker run --rm -v "$BUILD_DIR":/tmp "$DOCKER_IMAGE" /tmp/build.sh || exit 1 mv "$BUILD_DIR"pegh.static.openssl "./pegh-$ARCH-openssl" mv "$BUILD_DIR"pegh.static.libsodium "./pegh-$ARCH-libsodium" +mv "$BUILD_DIR"pegh.static.libsodium-universal-aes "./pegh-$ARCH-libsodium-universal-aes" rm -rf "$BUILD_DIR" 2>/dev/null exit 0 diff --git a/.gitignore b/.gitignore index 2b734c4..ab12291 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ pegh pegh.exe -pegh.libsodium +pegh.libsodium* pegh.openssl bla.txt diff --git a/Makefile b/Makefile index 37a2de0..3687d38 100644 --- a/Makefile +++ b/Makefile @@ -9,13 +9,24 @@ CFLAGS += -Wall -Wextra -Werror -std=c89 -pedantic \ -O3 ifdef PEGH_OPENSSL + +ifdef PEGH_LIBSODIUM +# both libsodium and openssl +CFLAGS += -DPEGH_LIBSODIUM -DPEGH_OPENSSL +LDFLAGS += -lsodium -lcrypto +else +# only openssl CFLAGS += -DPEGH_OPENSSL LDFLAGS += -lcrypto +endif + else ifdef PEGH_LIBSODIUM +# only libsodium CFLAGS += -DPEGH_LIBSODIUM LDFLAGS += -lsodium else +# default of only openssl CFLAGS += -DPEGH_OPENSSL LDFLAGS += -lcrypto endif diff --git a/pegh.c b/pegh.c index 7acdbdf..d34e0a6 100644 --- a/pegh.c +++ b/pegh.c @@ -76,6 +76,12 @@ #define IV_LEN 12 #define GCM_TAG_LEN 16 +/* libsodium only supports AES on specific platforms, this jazz is to fallback to openssl impls in those cases */ +typedef int (*gcm_func)(const unsigned char *, const size_t, + const unsigned char *, const unsigned char *, + unsigned char *, unsigned char * +); + /* default of OpenSSL for now... */ #if !defined(PEGH_OPENSSL) && !defined(PEGH_LIBSODIUM) #define PEGH_OPENSSL 1 @@ -104,7 +110,7 @@ static const size_t CHUNK_SIZE_MAX = INT_MAX; * 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, +int gcm_encrypt_openssl(const unsigned char *plaintext, const size_t plaintext_len, const unsigned char *key, const unsigned char *iv, unsigned char *ciphertext, @@ -178,7 +184,7 @@ int gcm_encrypt(const unsigned char *plaintext, const size_t plaintext_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, +int gcm_decrypt_openssl(const unsigned char *ciphertext, const size_t ciphertext_len, const unsigned char *key, const unsigned char *iv, unsigned char *tag, @@ -239,6 +245,10 @@ int gcm_decrypt(const unsigned char *ciphertext, const size_t ciphertext_len, return ret; } +/* if both PEGH_OPENSSL and PEGH_LIBSODIUM are defined, we only want the AES funcs from OpenSSL */ + +#ifndef PEGH_LIBSODIUM + /* 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, @@ -269,11 +279,16 @@ void wipe_memory(void * const ptr, const size_t len) { OPENSSL_cleanse(ptr, len); } -#endif +#endif /* PEGH_LIBSODIUM */ + +#endif /* PEGH_OPENSSL */ + #ifdef PEGH_LIBSODIUM #include +/* for now in hybrid mode lazily use OPENSSL's CHUNK_SIZE_MAX which is smaller, solution to make them the same coming soon */ +#ifndef PEGH_OPENSSL /* 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 */ /* // actually this is breaking on x86 and aarch64 where size_t is `unsigned int` and this overflows, how to handle??? @@ -281,6 +296,7 @@ static const size_t CHUNK_SIZE_MAX = 1024UL * 1024 * 1024 * 32; // for now, 4gb will do? */ static const size_t CHUNK_SIZE_MAX = UINT_MAX; +#endif /* PEGH_OPENSSL */ /* * returns 1 on success, 0 on failure @@ -295,7 +311,7 @@ static const size_t CHUNK_SIZE_MAX = UINT_MAX; * 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, +int gcm_encrypt_libsodium(const unsigned char *plaintext, const size_t plaintext_len, const unsigned char *key, const unsigned char *iv, unsigned char *ciphertext, @@ -323,7 +339,7 @@ int gcm_encrypt(const unsigned char *plaintext, const size_t plaintext_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, +int gcm_decrypt_libsodium(const unsigned char *ciphertext, const size_t ciphertext_len, const unsigned char *key, const unsigned char *iv, unsigned char *tag, @@ -382,6 +398,15 @@ void wipe_memory(void * const ptr, const size_t len) { sodium_memzero(ptr, len); } +#endif /* PEGH_LIBSODIUM */ + +/* always prefer libsodium AES if possible because it's faster */ +#ifdef PEGH_LIBSODIUM +gcm_func gcm_encrypt = gcm_encrypt_libsodium; +gcm_func gcm_decrypt = gcm_decrypt_libsodium; +#elif PEGH_OPENSSL +gcm_func gcm_encrypt = gcm_encrypt_openssl; +gcm_func gcm_decrypt = gcm_decrypt_openssl; #endif /* returns 1 on success, 0 on failure */ @@ -738,10 +763,18 @@ int main(int argc, char **argv) return 2; } if (crypto_aead_aes256gcm_is_available() == 0) { +#ifdef PEGH_OPENSSL + /* swap to OpenSSL AES which is always supported */ + fprintf(stderr, "Warning: libsodium does not support AES-256-GCM on this CPU, falling back to openssl version instead...\n"); + gcm_encrypt = gcm_encrypt_openssl; + gcm_decrypt = gcm_decrypt_openssl; +#else + /* nothing we can do */ fprintf(stderr, "Error: libsodium does not support AES-256-GCM on this CPU, compile/use openssl version?\n"); return 2; +#endif /* PEGH_OPENSSL */ } -#endif +#endif /* PEGH_LIBSODIUM */ for (optind = 1; optind < argc; ++optind) { if(strlen(argv[optind]) == 2 && argv[optind][0] == '-') { diff --git a/test.sh b/test.sh index 7e6721c..6320a7a 100755 --- a/test.sh +++ b/test.sh @@ -7,7 +7,7 @@ export dummy_mb="$1" [ "$dummy_file" = "" ] && export dummy_file='/tmp/randombytes' [ "$dummy_mb" = "" ] && export dummy_mb='100' -[ "$TEST_BINS" = "" ] && TEST_BINS="./pegh.openssl ./pegh.libsodium" +[ "$TEST_BINS" = "" ] && TEST_BINS="./pegh.openssl ./pegh.libsodium ./pegh.libsodium-universal-aes" set -euxo pipefail @@ -25,6 +25,10 @@ mv pegh pegh.openssl make PEGH_LIBSODIUM=1 || cc pegh.c -DPEGH_LIBSODIUM -lsodium -O3 -o pegh mv pegh pegh.libsodium +# compile against both libsodium and openssl as a fallback for CPUs libsodium doesn't support +make PEGH_LIBSODIUM=1 PEGH_OPENSSL=1 || cc pegh.c -DPEGH_LIBSODIUM -DPEGH_OPENSSL -lsodium -lcrypto -O3 -o pegh +mv pegh pegh.libsodium-universal-aes + export key="$(< /dev/urandom tr -dc 'a-z0-9' | head -c12)" echo "key: $key"