Skip to content

Commit

Permalink
Add option to dynamically load libcrypto.so.*
Browse files Browse the repository at this point in the history
This adds OQS_DLOPEN_OPENSSL build option to use OpenSSL through
dynamically loaded libcrypto.so.* with dlopen, instead of linking to
the library at build time.

That way the applications could use their favorite implementation of
common cryptographic primitives without pulling in the OpenSSL as a
hard dependency.

Signed-off-by: Daiki Ueno <[email protected]>
  • Loading branch information
ueno committed Mar 8, 2024
1 parent 6832336 commit a574880
Show file tree
Hide file tree
Showing 16 changed files with 438 additions and 257 deletions.
2 changes: 2 additions & 0 deletions .CMake/alg_support.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ cmake_dependent_option(OQS_USE_SHA3_OPENSSL "" OFF "OQS_USE_OPENSSL" OFF)
# sanity check: Disable OpenSSL if not a single OpenSSL component define is on
cmake_dependent_option(OQS_USE_OPENSSL "" ON "OQS_USE_AES_OPENSSL OR OQS_USE_SHA2_OPENSSL OR OQS_USE_SHA3_OPENSSL" OFF)

option(OQS_DLOPEN_OPENSSL "Enable OpenSSL through dlopen" OFF)

if(CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin")
if(OQS_DIST_X86_64_BUILD OR OQS_USE_AVX2_INSTRUCTIONS)
cmake_dependent_option(OQS_ENABLE_SHA3_xkcp_low_avx2 "" ON "NOT OQS_USE_SHA3_OPENSSL" OFF)
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ jobs:
container: openquantumsafe/ci-ubuntu-jammy:latest
CMAKE_ARGS: -DOQS_STRICT_WARNINGS=ON -DOQS_ALGS_ENABLED=STD -DBUILD_SHARED_LIBS=ON
PYTEST_ARGS: --ignore=tests/test_leaks.py --ignore=tests/test_kat_all.py
- name: jammy-std-openssl3-dlopen
container: openquantumsafe/ci-ubuntu-jammy:latest
CMAKE_ARGS: -DOQS_STRICT_WARNINGS=ON -DOQS_ALGS_ENABLED=STD -DBUILD_SHARED_LIBS=ON -DOQS_DLOPEN_OPENSSL=ON
PYTEST_ARGS: --ignore=tests/test_leaks.py --ignore=tests/test_kat_all.py
- name: address-sanitizer
container: openquantumsafe/ci-ubuntu-focal-x86_64:latest
CMAKE_ARGS: -DCMAKE_C_COMPILER=clang-9 -DCMAKE_BUILD_TYPE=Debug -DUSE_SANITIZER=Address
Expand Down
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,20 @@ if(${OQS_USE_OPENSSL})
endif()
endif()
find_package(OpenSSL 1.1.1 REQUIRED)

if(OQS_DLOPEN_OPENSSL)
find_program(OBJDUMP objdump)
if(NOT OBJDUMP)
message(FATAL_ERROR "objdump not found. Please install it from binutils.")
endif()
execute_process(
COMMAND ${OBJDUMP} -p ${OPENSSL_CRYPTO_LIBRARY}
COMMAND sed -n "s/[ ]\\{1,\\}SONAME[ ]\\{1,\\}//p"
OUTPUT_VARIABLE OQS_OPENSSL_CRYPTO_SONAME
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
message(STATUS "OpenSSL dlopen SONAME: " ${OQS_OPENSSL_CRYPTO_SONAME})
endif()
endif()

set(PUBLIC_HEADERS ${PROJECT_SOURCE_DIR}/src/oqs.h
Expand Down
10 changes: 9 additions & 1 deletion CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ These can be set to `ON` or `OFF` and take an effect if liboqs is built for use

## OQS_USE_OPENSSL

In order to save size and limit the mount of different cryptographic code bases, it is possible to use OpenSSL as a crypto code provider by setting this configuration option.
In order to save size and limit the amount of different cryptographic code bases, it is possible to use OpenSSL as a crypto code provider by setting this configuration option.

This can be set to `ON` or `OFF`. When `ON`, the additional options `OQS_USE_AES_OPENSSL`, `OQS_USE_SHA2_OPENSSL`, and `OQS_USE_SHA3_OPENSSL` are made available to control whether liboqs uses OpenSSL's AES, SHA-2, and SHA-3 implementations.

Expand All @@ -111,6 +111,14 @@ When `OQS_USE_OPENSSL` is `ON`, CMake also scans the filesystem to find the mini

**Default**: `ON`.

### OQS_DLOPEN_OPENSSL

Dynamically load OpenSSL through `dlopen`. When using liboqs from other cryptographic libraries, hard dependency on OpenSSL is sometimes undesirable. If this option is `ON`, loading of OpenSSL will be deferred until any of the OpenSSL functions is used.

Only has an effect if the system supports `dlopen` and ELF binary format, such as Linux or BSD family.

**Default**: `OFF`.

## OQS_OPT_TARGET

An optimization target. Only has an effect if the compiler is GCC or Clang and `OQS_DIST_BUILD=OFF`. Can take any valid input to the `-march` (on x86-64) or `-mcpu` (on ARM32v7 or ARM64v8) option for `CMAKE_C_COMPILER`. Can also be set to one of the following special values.
Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ if(DEFINED SANITIZER_LD_FLAGS)
target_link_libraries(oqs-internal PUBLIC ${SANITIZER_LD_FLAGS})
endif()
if(${OQS_USE_OPENSSL})
if(${OQS_DLOPEN_OPENSSL})
target_link_libraries(oqs PRIVATE ${CMAKE_DL_LIBS})
target_link_libraries(oqs-internal PRIVATE ${CMAKE_DL_LIBS})
else()
target_link_libraries(oqs PRIVATE ${OPENSSL_CRYPTO_LIBRARY})
target_link_libraries(oqs-internal PRIVATE ${OPENSSL_CRYPTO_LIBRARY})
endif()
endif()

target_include_directories(oqs
Expand Down
37 changes: 18 additions & 19 deletions src/common/aes/aes_ossl.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

#include "aes.h"

#include <openssl/evp.h>
#include "../ossl_helpers.h"

struct key_schedule {
Expand All @@ -33,17 +32,17 @@ static void AES128_ECB_load_schedule(const uint8_t *key, void **schedule) {
OQS_EXIT_IF_NULLPTR(*schedule, "OpenSSL");
struct key_schedule *ks = (struct key_schedule *) *schedule;
ks->for_ECB = 1;
ks->ctx = EVP_CIPHER_CTX_new();
ks->ctx = OSSL_FUNC(EVP_CIPHER_CTX_new)();
OQS_EXIT_IF_NULLPTR(ks->ctx, "OpenSSL");
OQS_OPENSSL_GUARD(EVP_EncryptInit_ex(ks->ctx, oqs_aes_128_ecb(), NULL, key, NULL));
EVP_CIPHER_CTX_set_padding(ks->ctx, 0);
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptInit_ex)(ks->ctx, oqs_aes_128_ecb(), NULL, key, NULL));
OSSL_FUNC(EVP_CIPHER_CTX_set_padding)(ks->ctx, 0);
}

static void AES128_free_schedule(void *schedule) {
if (schedule != NULL) {
struct key_schedule *ks = (struct key_schedule *) schedule;
if (ks->ctx != NULL) {
EVP_CIPHER_CTX_free(ks->ctx);
OSSL_FUNC(EVP_CIPHER_CTX_free)(ks->ctx);
}
OQS_MEM_cleanse(ks->key, 32);
OQS_MEM_secure_free(schedule, sizeof(struct key_schedule));
Expand All @@ -62,26 +61,26 @@ static void AES128_ECB_enc_sch(const uint8_t *plaintext, const size_t plaintext_
int outlen;
const struct key_schedule *ks = (const struct key_schedule *) schedule;
SIZE_T_TO_INT_OR_EXIT(plaintext_len, plaintext_len_int)
OQS_OPENSSL_GUARD(EVP_EncryptUpdate(ks->ctx, ciphertext, &outlen, plaintext, plaintext_len_int));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptUpdate)(ks->ctx, ciphertext, &outlen, plaintext, plaintext_len_int));
assert(outlen == plaintext_len_int);
OQS_OPENSSL_GUARD(EVP_EncryptFinal_ex(ks->ctx, ciphertext, &outlen));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptFinal_ex)(ks->ctx, ciphertext, &outlen));
}

static void AES256_ECB_load_schedule(const uint8_t *key, void **schedule) {
*schedule = malloc(sizeof(struct key_schedule));
OQS_EXIT_IF_NULLPTR(*schedule, "OpenSSL");
struct key_schedule *ks = (struct key_schedule *) *schedule;
ks->for_ECB = 1;
ks->ctx = EVP_CIPHER_CTX_new();
ks->ctx = OSSL_FUNC(EVP_CIPHER_CTX_new)();
OQS_EXIT_IF_NULLPTR(ks->ctx, "OpenSSL");
OQS_OPENSSL_GUARD(EVP_EncryptInit_ex(ks->ctx, oqs_aes_256_ecb(), NULL, key, NULL));
EVP_CIPHER_CTX_set_padding(ks->ctx, 0);
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptInit_ex)(ks->ctx, oqs_aes_256_ecb(), NULL, key, NULL));
OSSL_FUNC(EVP_CIPHER_CTX_set_padding)(ks->ctx, 0);
}

static void AES256_CTR_inc_init(const uint8_t *key, void **schedule) {
*schedule = malloc(sizeof(struct key_schedule));
struct key_schedule *ks = (struct key_schedule *) *schedule;
EVP_CIPHER_CTX *ctr_ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX *ctr_ctx = OSSL_FUNC(EVP_CIPHER_CTX_new)();
assert(ctr_ctx != NULL);

OQS_EXIT_IF_NULLPTR(*schedule, "OpenSSL");
Expand All @@ -101,15 +100,15 @@ static void AES256_CTR_inc_iv(const uint8_t *iv, size_t iv_len, void *schedule)
} else {
exit(EXIT_FAILURE);
}
OQS_OPENSSL_GUARD(EVP_EncryptInit_ex(ks->ctx, oqs_aes_256_ctr(), NULL, ks->key, ks->iv));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptInit_ex)(ks->ctx, oqs_aes_256_ctr(), NULL, ks->key, ks->iv));
}

static void AES256_CTR_inc_ivu64(uint64_t iv, void *schedule) {
OQS_EXIT_IF_NULLPTR(schedule, "OpenSSL");
struct key_schedule *ks = (struct key_schedule *) schedule;
br_enc64be(ks->iv, iv);
memset(&ks->iv[8], 0, 8);
OQS_OPENSSL_GUARD(EVP_EncryptInit_ex(ks->ctx, oqs_aes_256_ctr(), NULL, ks->key, ks->iv));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptInit_ex)(ks->ctx, oqs_aes_256_ctr(), NULL, ks->key, ks->iv));
}

static void AES256_free_schedule(void *schedule) {
Expand All @@ -130,7 +129,7 @@ static void AES256_ECB_enc_sch(const uint8_t *plaintext, const size_t plaintext_
}

static void AES256_CTR_inc_stream_iv(const uint8_t *iv, size_t iv_len, const void *schedule, uint8_t *out, size_t out_len) {
EVP_CIPHER_CTX *ctr_ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX *ctr_ctx = OSSL_FUNC(EVP_CIPHER_CTX_new)();
assert(ctr_ctx != NULL);
uint8_t iv_ctr[16];
if (iv_len == 12) {
Expand All @@ -145,14 +144,14 @@ static void AES256_CTR_inc_stream_iv(const uint8_t *iv, size_t iv_len, const voi
exit(EXIT_FAILURE);
}
const struct key_schedule *ks = (const struct key_schedule *) schedule;
OQS_OPENSSL_GUARD(EVP_EncryptInit_ex(ctr_ctx, oqs_aes_256_ctr(), NULL, ks->key, iv_ctr));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptInit_ex)(ctr_ctx, oqs_aes_256_ctr(), NULL, ks->key, iv_ctr));

SIZE_T_TO_INT_OR_EXIT(out_len, out_len_input_int)
memset(out, 0, (size_t)out_len_input_int);
int out_len_output;
OQS_OPENSSL_GUARD(EVP_EncryptUpdate(ctr_ctx, out, &out_len_output, out, out_len_input_int));
OQS_OPENSSL_GUARD(EVP_EncryptFinal_ex(ctr_ctx, out + out_len_output, &out_len_output));
EVP_CIPHER_CTX_free(ctr_ctx);
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptUpdate)(ctr_ctx, out, &out_len_output, out, out_len_input_int));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptFinal_ex)(ctr_ctx, out + out_len_output, &out_len_output));
OSSL_FUNC(EVP_CIPHER_CTX_free)(ctr_ctx);
}

static void AES256_CTR_inc_stream_blks(void *schedule, uint8_t *out, size_t out_blks) {
Expand All @@ -161,7 +160,7 @@ static void AES256_CTR_inc_stream_blks(void *schedule, uint8_t *out, size_t out_
int out_len_output;
SIZE_T_TO_INT_OR_EXIT(out_len, out_len_input_int);
memset(out, 0, (size_t)out_len_input_int);
OQS_OPENSSL_GUARD(EVP_EncryptUpdate(ks->ctx, out, &out_len_output, out, (int) out_len));
OQS_OPENSSL_GUARD(OSSL_FUNC(EVP_EncryptUpdate)(ks->ctx, out, &out_len_output, out, (int) out_len));
}

struct OQS_AES_callbacks aes_default_callbacks = {
Expand Down
2 changes: 0 additions & 2 deletions src/common/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#endif

#if defined(OQS_USE_OPENSSL)
#include <openssl/evp.h>
#include "ossl_helpers.h"
#endif

Expand Down Expand Up @@ -232,7 +231,6 @@ OQS_API void OQS_init(void) {
#if defined(OQS_DIST_BUILD)
OQS_CPU_has_extension(OQS_CPU_EXT_INIT);
#endif
return;
}

OQS_API const char *OQS_version(void) {
Expand Down
50 changes: 50 additions & 0 deletions src/common/ossl_functions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT

// This file lists all OpenSSL functions used throughout the liboqs source code.
//
// Note that this file is included multiple times to generate custom
// code by definining the FUNC macro, so no header guard should be
// added here.

VOID_FUNC(void, ERR_print_errors_fp, (FILE *fp), (fp))
VOID_FUNC(void, EVP_CIPHER_CTX_free, (EVP_CIPHER_CTX *c), (c))
FUNC(EVP_CIPHER_CTX *, EVP_CIPHER_CTX_new, (void), ())
FUNC(int, EVP_CIPHER_CTX_set_padding, (EVP_CIPHER_CTX *c, int pad), (c, pad))
FUNC(int, EVP_DigestFinalXOF, (EVP_MD_CTX *ctx, unsigned char *md, size_t len), (ctx, md, len))
FUNC(int, EVP_DigestFinal_ex, (EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s), (ctx, md, s))
FUNC(int, EVP_DigestInit_ex, (EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl), (ctx, type, impl))
FUNC(int, EVP_DigestUpdate, (EVP_MD_CTX *ctx, const void *d, size_t cnt), (ctx, d, cnt))
FUNC(int, EVP_EncryptFinal_ex, (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl), (ctx, out, outl))
FUNC(int, EVP_EncryptInit_ex, (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv),
(ctx, cipher, impl, key, iv))
FUNC(int, EVP_EncryptUpdate, (EVP_CIPHER_CTX *ctx, unsigned char *out,
int *outl, const unsigned char *in, int inl),
(ctx, out, outl, in, inl))
FUNC(int, EVP_MD_CTX_copy_ex, (EVP_MD_CTX *out, const EVP_MD_CTX *in), (out, in))
VOID_FUNC(void, EVP_MD_CTX_free, (EVP_MD_CTX *ctx), (ctx))
FUNC(EVP_MD_CTX *, EVP_MD_CTX_new, (void), ())
FUNC(const EVP_CIPHER *, EVP_aes_128_ecb, (void), ())
FUNC(const EVP_CIPHER *, EVP_aes_256_ecb, (void), ())
FUNC(const EVP_CIPHER *, EVP_aes_256_ctr, (void), ())
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
FUNC(EVP_CIPHER *, EVP_CIPHER_fetch, (OSSL_LIB_CTX *ctx, const char *algorithm,
const char *properties),
(ctx, algorithm, properties))
VOID_FUNC(void, EVP_CIPHER_free, (EVP_CIPHER *cipher), (cipher))
FUNC(EVP_MD *, EVP_MD_fetch, (OSSL_LIB_CTX *ctx, const char *algorithm,
const char *properties),
(ctx, algorithm, properties))
VOID_FUNC(void, EVP_MD_free, (EVP_MD *md), (md))
#else
FUNC(const EVP_MD *, EVP_sha256, (void), ())
FUNC(const EVP_MD *, EVP_sha384, (void), ())
FUNC(const EVP_MD *, EVP_sha3_256, (void), ())
FUNC(const EVP_MD *, EVP_sha3_384, (void), ())
FUNC(const EVP_MD *, EVP_sha3_512, (void), ())
FUNC(const EVP_MD *, EVP_sha512, (void), ())
FUNC(const EVP_MD *, EVP_shake128, (void), ())
FUNC(const EVP_MD *, EVP_shake256, (void), ())
#endif
FUNC(int, RAND_bytes, (unsigned char *buf, int num), (buf, num))
FUNC(int, RAND_poll, (void), ())
FUNC(int, RAND_status, (void), ())
Loading

0 comments on commit a574880

Please sign in to comment.