Skip to content

Commit ae5f85e

Browse files
committed
add support for Linux systems shipping with OpenSSL 3
Fixes #47
1 parent 0c37d1a commit ae5f85e

File tree

5 files changed

+131
-10
lines changed

5 files changed

+131
-10
lines changed

.appveyor.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ build_script:
1010
-DCMAKE_TOOLCHAIN_FILE=~/vcpkg/scripts/buildsystems/vcpkg.cmake
1111
-DVCPKG_TARGET_TRIPLET=arch-env -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$TOOLCHAIN
1212
-DCMAKE_OSX_ARCHITECTURES=$ARCH -DCMAKE_OSX_DEPLOYMENT_TARGET=$DEPLOY_TARGET
13+
-DRUNTIME_OPENSSL=YES
1314
- cmake --build build
1415

1516
test_script:

CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
cmake_minimum_required(VERSION 3.15)
22

3+
option(RUNTIME_OPENSSL
4+
"Load OpenSSL at runtime instead of linking against a specific version" OFF)
5+
36
if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
47
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
58
CACHE STRING "")

src/CMakeLists.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,11 @@ target_link_libraries(reapack
139139
)
140140

141141
if(OPENSSL_FOUND)
142-
target_link_libraries(reapack OpenSSL::Crypto)
142+
if(RUNTIME_OPENSSL)
143+
target_compile_definitions(reapack PRIVATE RUNTIME_OPENSSL)
144+
else()
145+
target_link_libraries(reapack OpenSSL::Crypto)
146+
endif()
143147
endif()
144148

145149
if(SWELL_FOUND)

src/hash.cpp

+121-9
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,9 @@ class Hash::CNGContext : public Hash::Context {
111111
unsigned long m_hashLength;
112112
};
113113

114-
#else // Unix systems
115-
116-
# ifdef __APPLE__
117-
# define COMMON_DIGEST_FOR_OPENSSL
118-
# include <CommonCrypto/CommonDigest.h>
119-
# else
120-
# include <openssl/sha.h>
121-
# endif
114+
#elif __APPLE__
115+
# define COMMON_DIGEST_FOR_OPENSSL
116+
# include <CommonCrypto/CommonDigest.h>
122117

123118
class Hash::SHA256Context : public Hash::Context {
124119
public:
@@ -146,6 +141,120 @@ class Hash::SHA256Context : public Hash::Context {
146141
SHA256_CTX m_context;
147142
};
148143

144+
#else
145+
# include <openssl/evp.h>
146+
147+
# ifdef RUNTIME_OPENSSL
148+
# include <dlfcn.h>
149+
150+
static struct OpenSSL {
151+
OpenSSL()
152+
: m_loaded { true }
153+
{
154+
constexpr const char *names[] { "libcrypto.so.3", "libcrypto.so.1.1" };
155+
156+
for(const char *name : names) {
157+
if((m_so = dlopen(name, RTLD_LAZY)))
158+
return;
159+
}
160+
161+
m_loaded = false;
162+
}
163+
164+
~OpenSSL()
165+
{
166+
if(m_so)
167+
dlclose(m_so);
168+
}
169+
170+
bool isLoaded() const { return m_loaded; }
171+
172+
template<typename T> T get(const char *name)
173+
{
174+
const T func { m_so ? reinterpret_cast<T>(dlsym(m_so, name)) : nullptr };
175+
if(!func)
176+
m_loaded = false;
177+
return func;
178+
}
179+
180+
private:
181+
void *m_so;
182+
bool m_loaded;
183+
} g_openssl;
184+
185+
# define IMPORT_OPENSSL(func) \
186+
static auto _##func { g_openssl.get<decltype(&func)>(#func) };
187+
188+
IMPORT_OPENSSL(EVP_DigestFinal_ex);
189+
# define EVP_DigestFinal_ex _EVP_DigestFinal_ex
190+
IMPORT_OPENSSL(EVP_DigestInit_ex);
191+
# define EVP_DigestInit_ex _EVP_DigestInit_ex
192+
IMPORT_OPENSSL(EVP_DigestUpdate);
193+
# define EVP_DigestUpdate _EVP_DigestUpdate
194+
IMPORT_OPENSSL(EVP_MD_CTX_new);
195+
# define EVP_MD_CTX_new _EVP_MD_CTX_new
196+
IMPORT_OPENSSL(EVP_MD_CTX_free);
197+
# define EVP_MD_CTX_free _EVP_MD_CTX_free
198+
# ifdef EVP_MD_size // OpenSSL 3
199+
IMPORT_OPENSSL(EVP_MD_get_size);
200+
# undef EVP_MD_size
201+
# define EVP_MD_size _EVP_MD_get_size
202+
# else
203+
IMPORT_OPENSSL(EVP_MD_size);
204+
# define EVP_MD_size _EVP_MD_size
205+
# endif
206+
IMPORT_OPENSSL(EVP_sha256);
207+
# define EVP_sha256 _EVP_sha256
208+
# endif
209+
210+
class Hash::EVPContext : public Hash::Context {
211+
public:
212+
static const EVP_MD *getAlgo(const Algorithm algo)
213+
{
214+
#ifdef RUNTIME_OPENSSL
215+
if(!g_openssl.isLoaded())
216+
return nullptr;
217+
#endif
218+
219+
switch(algo) {
220+
case SHA256:
221+
return EVP_sha256();
222+
default:
223+
return nullptr;
224+
}
225+
}
226+
227+
EVPContext(const EVP_MD *md)
228+
: m_md { md }
229+
{
230+
m_ctx = EVP_MD_CTX_new();
231+
EVP_DigestInit_ex(m_ctx, m_md, nullptr);
232+
}
233+
234+
~EVPContext()
235+
{
236+
EVP_MD_CTX_free(m_ctx);
237+
}
238+
239+
size_t hashSize() const override
240+
{
241+
return EVP_MD_size(m_md);
242+
}
243+
244+
void addData(const char *data, const size_t len) override
245+
{
246+
EVP_DigestUpdate(m_ctx, data, len);
247+
}
248+
249+
void getHash(unsigned char *out) override
250+
{
251+
EVP_DigestFinal_ex(m_ctx, out, nullptr);
252+
}
253+
254+
private:
255+
EVP_MD_CTX *m_ctx;
256+
const EVP_MD *m_md;
257+
};
149258
#endif
150259

151260
Hash::Hash(const Algorithm algo)
@@ -154,12 +263,15 @@ Hash::Hash(const Algorithm algo)
154263
#ifdef _WIN32
155264
if(const auto &algoProvider = CNGAlgorithmProvider::get(algo))
156265
m_context = std::make_unique<CNGContext>(algoProvider);
157-
#else
266+
#elif __APPLE__
158267
switch(algo) {
159268
case SHA256:
160269
m_context = std::make_unique<SHA256Context>();
161270
break;
162271
}
272+
#else
273+
if(const auto &algoDesc = EVPContext::getAlgo(algo))
274+
m_context = std::make_unique<EVPContext>(algoDesc);
163275
#endif
164276
}
165277

src/hash.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class Hash {
4646

4747
class CNGContext;
4848
class SHA256Context;
49+
class EVPContext;
4950

5051
Algorithm m_algo;
5152
std::string m_value;

0 commit comments

Comments
 (0)