Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,12 +1,36 @@
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")

bool_flag(
name = "bssl_libdecrepit_missing",
build_setting_default = False,
)

config_setting(
name = "cfg_bssl_libdecrepit_missing",
flag_values = {
"bssl_libdecrepit_missing": "True",
},
)

cc_library(
name = "ncrypto",
srcs = glob(["src/*.cpp"]),
hdrs = glob(["include/*.h"]),
includes = ["include"],
local_defines = {
"NCRYPTO_BSSL_LIBDECREPIT_MISSING": select(
{
":cfg_bssl_libdecrepit_missing": 1,
"//conditions:default": 0,
},
),
},
visibility = ["//visibility:public"],
deps = [
"@ssl//:ssl",
"@ssl//:crypto",
"@ssl//:decrepit"
]
"@ssl",
"@ssl//:crypto",
] + select({
":cfg_bssl_libdecrepit_missing": [],
"//conditions:default": ["@ssl//:decrepit"],
}),
)
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ include(cmake/CPM.cmake)

CPMAddPackage(
NAME boringssl
VERSION 0.20250114.0
VERSION 0.20250818.0
GITHUB_REPOSITORY google/boringssl
GIT_TAG 0.20250114.0
GIT_TAG 0.20250818.0
OPTIONS "BUILD_SHARED_LIBS OFF" "BUILD_TESTING OFF"
)
add_subdirectory(src)
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Node.js crypto (ncrypto) library

The `ncrypto` library extracts the base internal implementation of Node.js crypto operations
that support both `node:crypto` and Web Crypto implementations and makes them available for
use in other projects that need to emulate Node.js' behavior.

## Compatibility

* Build systems: `ncrypto` can be built with either Bazel or CMake.
* SSL libraries: `ncrypto` supports linking to either `boringssl` or `openssl`.

## Build flags

* Bazel: `--@ncrypto//:bssl_libdecrepit_missing=True`
* CMake: `-DNCRYPTO_BSSL_LIBDECREPIT_MISSING=1`

If linking against `boringssl`, libdecrepit is an optional component that isn't always built. If
this option is set, `ncrypto` will use a built-in replacement for the missing functions.
1 change: 1 addition & 0 deletions cmake/ncrypto-flags.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
option(NCRYPTO_DEVELOPMENT_CHECKS "development checks (useful for debugging)" OFF)
option(NCRYPTO_TESTING "Build tests" ON)
option(NCRYPTO_BSSL_LIBDECREPIT_MISSING "enable if boringssl is built without libdecrepit" OFF)

set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
Expand Down
7 changes: 7 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
add_library(ncrypto ncrypto.cpp engine.cpp)
target_link_libraries(ncrypto PUBLIC ssl crypto)

if (NCRYPTO_BSSL_LIBDECREPIT_MISSING)
target_compile_definitions(ncrypto PUBLIC NCRYPTO_BSSL_LIBDECREPIT_MISSING=1)
else()
target_link_libraries(ncrypto PUBLIC decrepit)
endif()

target_include_directories(ncrypto
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
Expand Down
62 changes: 62 additions & 0 deletions src/ncrypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5297,3 +5297,65 @@ BIGNUM* BN_get_rfc3526_prime_8192(BIGNUM* ret) {
return get_params(ret, kWords, OPENSSL_ARRAY_SIZE(kWords));
}
#endif

// ===========================================================================
#if NCRYPTO_BSSL_LIBDECREPIT_MISSING
// While BoringSSL implements EVP_CIPHER_do_all_sorted, it includes this
// inplementation in a library called "libdecrepit", which might not be included
// depending on how boringssl was built. In such cases, a copy of the function
// is provided here:

extern "C" void EVP_CIPHER_do_all_sorted(
void (*callback)(const EVP_CIPHER* cipher,
const char* name,
const char* unused,
void* arg),
void* arg) {
callback(EVP_aes_128_cbc(), "AES-128-CBC", NULL, arg);
callback(EVP_aes_192_cbc(), "AES-192-CBC", NULL, arg);
callback(EVP_aes_256_cbc(), "AES-256-CBC", NULL, arg);
callback(EVP_aes_128_ctr(), "AES-128-CTR", NULL, arg);
callback(EVP_aes_192_ctr(), "AES-192-CTR", NULL, arg);
callback(EVP_aes_256_ctr(), "AES-256-CTR", NULL, arg);
callback(EVP_aes_128_ecb(), "AES-128-ECB", NULL, arg);
callback(EVP_aes_192_ecb(), "AES-192-ECB", NULL, arg);
callback(EVP_aes_256_ecb(), "AES-256-ECB", NULL, arg);
callback(EVP_aes_128_ofb(), "AES-128-OFB", NULL, arg);
callback(EVP_aes_192_ofb(), "AES-192-OFB", NULL, arg);
callback(EVP_aes_256_ofb(), "AES-256-OFB", NULL, arg);
callback(EVP_aes_128_gcm(), "AES-128-GCM", NULL, arg);
callback(EVP_aes_192_gcm(), "AES-192-GCM", NULL, arg);
callback(EVP_aes_256_gcm(), "AES-256-GCM", NULL, arg);
callback(EVP_des_cbc(), "DES-CBC", NULL, arg);
callback(EVP_des_ecb(), "DES-ECB", NULL, arg);
callback(EVP_des_ede(), "DES-EDE", NULL, arg);
callback(EVP_des_ede_cbc(), "DES-EDE-CBC", NULL, arg);
callback(EVP_des_ede3_cbc(), "DES-EDE3-CBC", NULL, arg);
callback(EVP_rc2_cbc(), "RC2-CBC", NULL, arg);
callback(EVP_rc4(), "RC4", NULL, arg);

// OpenSSL returns everything twice, the second time in lower case.
callback(EVP_aes_128_cbc(), "aes-128-cbc", NULL, arg);
callback(EVP_aes_192_cbc(), "aes-192-cbc", NULL, arg);
callback(EVP_aes_256_cbc(), "aes-256-cbc", NULL, arg);
callback(EVP_aes_128_ctr(), "aes-128-ctr", NULL, arg);
callback(EVP_aes_192_ctr(), "aes-192-ctr", NULL, arg);
callback(EVP_aes_256_ctr(), "aes-256-ctr", NULL, arg);
callback(EVP_aes_128_ecb(), "aes-128-ecb", NULL, arg);
callback(EVP_aes_192_ecb(), "aes-192-ecb", NULL, arg);
callback(EVP_aes_256_ecb(), "aes-256-ecb", NULL, arg);
callback(EVP_aes_128_ofb(), "aes-128-ofb", NULL, arg);
callback(EVP_aes_192_ofb(), "aes-192-ofb", NULL, arg);
callback(EVP_aes_256_ofb(), "aes-256-ofb", NULL, arg);
callback(EVP_aes_128_gcm(), "aes-128-gcm", NULL, arg);
callback(EVP_aes_192_gcm(), "aes-192-gcm", NULL, arg);
callback(EVP_aes_256_gcm(), "aes-256-gcm", NULL, arg);
callback(EVP_des_cbc(), "des-cbc", NULL, arg);
callback(EVP_des_ecb(), "des-ecb", NULL, arg);
callback(EVP_des_ede(), "des-ede", NULL, arg);
callback(EVP_des_ede_cbc(), "des-ede-cbc", NULL, arg);
callback(EVP_des_ede3_cbc(), "des-ede3-cbc", NULL, arg);
callback(EVP_rc2_cbc(), "rc2-cbc", NULL, arg);
callback(EVP_rc4(), "rc4", NULL, arg);
}
#endif
18 changes: 16 additions & 2 deletions tests/basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

#include <gtest/gtest.h>

TEST(basic, test_it) {
SUCCEED();
#include <string>
#include <unordered_set>

using namespace ncrypto;

TEST(basic, cipher_foreach) {
std::unordered_set<std::string> foundCiphers;

Cipher::ForEach([&](const char* name) { foundCiphers.insert(name); });

// When testing Cipher::ForEach, we cannot expect a particular list of ciphers
// as that depends on openssl vs boringssl, versions, configuration, etc.
// Instead, we look for a couple of very common ciphers that should always be
// present.
ASSERT_TRUE(foundCiphers.count("AES-128-CTR"));
ASSERT_TRUE(foundCiphers.count("AES-256-CBC"));
}