Skip to content

Commit a82a1db

Browse files
authored
Support powering the Crypto logic by a system OpenSSL (#2248)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 0a1cda9 commit a82a1db

File tree

6 files changed

+107
-10
lines changed

6 files changed

+107
-10
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ jobs:
3636
type: static
3737
shell: sh
3838
benchmark: linux/llvm
39+
- os: ubuntu-latest
40+
cc: clang
41+
cxx: clang++
42+
type: static
43+
shell: sh
44+
options: -DSOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL:BOOL=ON
3945
- os: ubuntu-latest
4046
cc: gcc
4147
cxx: g++

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ option(SOURCEMETA_CORE_UNICODE "Build the Sourcemeta Core Unicode library" ON)
1111
option(SOURCEMETA_CORE_PUNYCODE "Build the Sourcemeta Core Punycode library" ON)
1212
option(SOURCEMETA_CORE_TIME "Build the Sourcemeta Core time library" ON)
1313
option(SOURCEMETA_CORE_CRYPTO "Build the Sourcemeta Core Crypto library" ON)
14+
option(SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL "Use system OpenSSL for the Sourcemeta Core Crypto library" OFF)
1415
option(SOURCEMETA_CORE_REGEX "Build the Sourcemeta Core Regex library" ON)
1516
option(SOURCEMETA_CORE_URI "Build the Sourcemeta Core URI library" ON)
1617
option(SOURCEMETA_CORE_URITEMPLATE "Build the Sourcemeta Core URI Template library" ON)
@@ -89,6 +90,9 @@ if(SOURCEMETA_CORE_TIME)
8990
endif()
9091

9192
if(SOURCEMETA_CORE_CRYPTO)
93+
if(SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL)
94+
find_package(OpenSSL REQUIRED)
95+
endif()
9296
add_subdirectory(src/core/crypto)
9397
endif()
9498

config.cmake.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS})
4848
elseif(component STREQUAL "time")
4949
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_time.cmake")
5050
elseif(component STREQUAL "crypto")
51+
if(@SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL@)
52+
find_dependency(OpenSSL)
53+
endif()
5154
include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_crypto.cmake")
5255
elseif(component STREQUAL "regex")
5356
find_dependency(PCRE2 CONFIG)

src/core/crypto/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME crypto
22
PRIVATE_HEADERS sha256.h uuid.h
33
SOURCES crypto_sha256.cc crypto_uuid.cc)
44

5+
if(SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL)
6+
target_compile_definitions(sourcemeta_core_crypto
7+
PRIVATE SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL)
8+
target_link_libraries(sourcemeta_core_crypto PRIVATE OpenSSL::Crypto)
9+
endif()
10+
511
if(SOURCEMETA_CORE_INSTALL)
612
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT core NAME crypto)
713
endif()

src/core/crypto/crypto_sha256.cc

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,54 @@
22

33
#include <array> // std::array
44
#include <cstdint> // std::uint32_t, std::uint64_t
5+
6+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
7+
#include <openssl/evp.h> // EVP_MD_CTX_new, EVP_DigestInit_ex, EVP_sha256, EVP_DigestUpdate, EVP_DigestFinal_ex, EVP_MD_CTX_free
8+
#include <stdexcept> // std::runtime_error
9+
#else
510
#include <cstring> // std::memcpy
11+
#endif
12+
13+
namespace {
14+
constexpr std::array<char, 17> HEX_DIGITS{{'0', '1', '2', '3', '4', '5', '6',
15+
'7', '8', '9', 'a', 'b', 'c', 'd',
16+
'e', 'f', '\0'}};
17+
} // namespace
18+
19+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
20+
21+
namespace sourcemeta::core {
22+
23+
auto sha256(const std::string_view input, std::ostream &output) -> void {
24+
auto *context = EVP_MD_CTX_new();
25+
if (context == nullptr) {
26+
throw std::runtime_error("Could not allocate OpenSSL digest context");
27+
}
28+
29+
if (EVP_DigestInit_ex(context, EVP_sha256(), nullptr) != 1 ||
30+
EVP_DigestUpdate(context, input.data(), input.size()) != 1) {
31+
EVP_MD_CTX_free(context);
32+
throw std::runtime_error("Could not compute SHA-256 digest");
33+
}
34+
35+
std::array<unsigned char, 32> digest{};
36+
unsigned int length = 0;
37+
if (EVP_DigestFinal_ex(context, digest.data(), &length) != 1) {
38+
EVP_MD_CTX_free(context);
39+
throw std::runtime_error("Could not finalize SHA-256 digest");
40+
}
41+
42+
EVP_MD_CTX_free(context);
43+
44+
for (std::uint64_t index = 0; index < 32u; ++index) {
45+
output.put(HEX_DIGITS[(digest[index] >> 4u) & 0x0fu]);
46+
output.put(HEX_DIGITS[digest[index] & 0x0fu]);
47+
}
48+
}
49+
50+
} // namespace sourcemeta::core
51+
52+
#else
653

754
namespace {
855

@@ -169,17 +216,15 @@ auto sha256(const std::string_view input, std::ostream &output) -> void {
169216
sha256_process_block(final_block.data() + 64u, state);
170217
}
171218

172-
// Produce the final hex digest directly from state words (big-endian)
173-
static constexpr std::array<char, 17> hex_digits{
174-
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
175-
'e', 'f', '\0'}};
176219
for (std::uint64_t state_index = 0u; state_index < 8u; ++state_index) {
177220
const auto value = state[state_index];
178221
for (std::uint64_t nibble = 0u; nibble < 8u; ++nibble) {
179222
const auto shift = 28u - nibble * 4u;
180-
output.put(hex_digits[(value >> shift) & 0x0fu]);
223+
output.put(HEX_DIGITS[(value >> shift) & 0x0fu]);
181224
}
182225
}
183226
}
184227

185228
} // namespace sourcemeta::core
229+
230+
#endif

src/core/crypto/crypto_uuid.cc

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,78 @@
11
#include <sourcemeta/core/crypto_uuid.h>
22

3-
#include <array> // std::array
4-
#include <cstddef> // std::size_t
5-
#include <random> // std::random_device, std::mt19937, std::uniform_int_distribution
3+
#include <array> // std::array
4+
#include <cstddef> // std::size_t
65
#include <string_view> // std::string_view
76

7+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
8+
#include <openssl/rand.h> // RAND_bytes
9+
#include <stdexcept> // std::runtime_error
10+
#else
11+
#include <random> // std::random_device, std::mt19937, std::uniform_int_distribution
12+
#endif
13+
814
namespace sourcemeta::core {
915

1016
// See RFC 9562 Section 5.4
1117
// Format: xxxxxxxx-xxxx-4xxx-Nxxx-xxxxxxxxxxxx
1218
// where 4 is the version and N is the variant (8, 9, a, or b)
1319
auto uuidv4() -> std::string {
14-
static std::random_device device;
15-
static std::mt19937 generator{device()};
1620
static constexpr std::string_view digits = "0123456789abcdef";
1721
static constexpr std::string_view variant_digits = "89ab";
1822
static constexpr std::array<bool, 16> dash = {
1923
{false, false, false, false, true, false, true, false, true, false, true,
2024
false, false, false, false, false}};
25+
26+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
27+
std::array<unsigned char, 16> random_bytes{};
28+
if (RAND_bytes(random_bytes.data(), static_cast<int>(random_bytes.size())) !=
29+
1) {
30+
throw std::runtime_error("Could not generate random bytes with OpenSSL");
31+
}
32+
#else
33+
static std::random_device device;
34+
static std::mt19937 generator{device()};
2135
std::uniform_int_distribution<decltype(digits)::size_type> distribution(0,
2236
15);
2337
std::uniform_int_distribution<decltype(variant_digits)::size_type>
2438
variant_distribution(0, 3);
39+
#endif
40+
2541
std::string result;
2642
result.reserve(36);
2743
for (std::size_t index = 0; index < dash.size(); ++index) {
2844
if (dash[index]) {
2945
result += '-';
3046
}
3147

48+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
49+
const auto high_nibble = (random_bytes[index] >> 4u) & 0x0fu;
50+
const auto low_nibble = random_bytes[index] & 0x0fu;
51+
#endif
52+
3253
// RFC 9562 Section 5.4: version bits (48-51) must be 0b0100
3354
if (index == 6) {
3455
result += '4';
3556
// RFC 9562 Section 5.4: variant bits (64-65) must be 0b10
3657
} else if (index == 8) {
58+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
59+
result += variant_digits[high_nibble & 0x03u];
60+
#else
3761
result += variant_digits[variant_distribution(generator)];
62+
#endif
3863
} else {
64+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
65+
result += digits[high_nibble];
66+
#else
3967
result += digits[distribution(generator)];
68+
#endif
4069
}
4170

71+
#ifdef SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL
72+
result += digits[low_nibble];
73+
#else
4274
result += digits[distribution(generator)];
75+
#endif
4376
}
4477

4578
return result;

0 commit comments

Comments
 (0)