Skip to content
Draft
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
5 changes: 3 additions & 2 deletions .github/workflows/test-shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,9 @@ jobs:
# the matrix-selected nixpkgs attribute (e.g. `openssl_3_6`). All
# other shared libs (brotli, cares, libuv, …) keep their defaults.
# `permittedInsecurePackages` whitelists just the matrix-selected
# release (e.g. `openssl-1.1.1w`) so EOL-with-extended-support
# cycles evaluate without relaxing nixpkgs' meta check globally.
# releases so EOL-with-extended-support cycles evaluate without
# relaxing nixpkgs' meta check globally. It is empty while every
# matrix entry is a supported release.
extra-nix-flags: |
--arg useSeparateDerivationForV8 ${{ needs.build-aarch64-linux-v8.outputs.local-cache && '"$(nix-store --import < libv8-aarch64-linux.nar)"' || 'true' }} \
--arg sharedLibDeps "(import $TAR_DIR/tools/nix/sharedLibDeps.nix {}) // {
Expand Down
30 changes: 20 additions & 10 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,24 +206,22 @@ on your Linux distribution.

#### OpenSSL asm support

OpenSSL-1.1.1 requires the following assembler version for use of asm
OpenSSL requires the following assembler version for use of asm
support on x86\_64 and ia32.

For use of AVX-512,

* gas (GNU assembler) version 2.26 or higher
* nasm version 2.11.8 or higher in Windows

AVX-512 is disabled for Skylake-X by OpenSSL-1.1.1.

For use of AVX2,

* gas (GNU assembler) version 2.23 or higher
* Xcode version 5.0 or higher
* llvm version 3.3 or higher
* nasm version 2.10 or higher in Windows

Please refer to <https://docs.openssl.org/1.1.1/man3/OPENSSL_ia32cap/> for details.
Please refer to <https://docs.openssl.org/master/man3/OPENSSL_ia32cap/> for details.

If compiling without one of the above, use `configure` with the
`--openssl-no-asm` flag. Otherwise, `configure` will fail.
Expand Down Expand Up @@ -1032,7 +1030,7 @@ as `deps/icu` (You'll have: `deps/icu/source/...`)
### Configure OpenSSL appname

Node.js can use an OpenSSL configuration file by specifying the environment
variable `OPENSSL_CONF`, or using the command line option `--openssl-conf`, and
variable `OPENSSL_CONF`, or using the command line option `--openssl-config`, and
if none of those are specified will default to reading the default OpenSSL
configuration file `openssl.cnf`. Node.js will only read a section that is by
default named `nodejs_conf`, but this name can be overridden using the following
Expand All @@ -1044,12 +1042,20 @@ configure option:

## Building Node.js with FIPS-compliant OpenSSL

Node.js supports FIPS when statically or dynamically linked with OpenSSL 3 via
[OpenSSL's provider model](https://docs.openssl.org/3.0/man7/crypto/#OPENSSL-PROVIDERS).
It is not necessary to rebuild Node.js to enable support for FIPS.
Node.js can use an OpenSSL FIPS provider via
[OpenSSL's provider model](https://docs.openssl.org/master/man7/crypto/#openssl-providers),
whether OpenSSL is linked statically or dynamically. It is not necessary to
rebuild Node.js to do so; the provider and the OpenSSL configuration that
activates it are supplied at runtime.

Node.js does not build a FIPS provider. OpenSSL requires that a FIPS provider
be built from a release that carries a FIPS certificate, so a provider built
as part of the Node.js build would have no validation status.

See [FIPS mode](doc/api/crypto.md#fips-mode) for more information on how to
enable FIPS support in Node.js.
`./configure --openssl-is-fips` only records that the OpenSSL being linked is
FIPS capable, and requires `--shared-openssl`.

See [FIPS mode](doc/api/crypto.md#fips-mode) for how to configure it.

## Building Node.js with Temporal support

Expand Down Expand Up @@ -1133,6 +1139,10 @@ A number of `configure` options are provided to support this use case.
provide the ability to set the path to an external JavaScript file
for the dependency to be used at runtime.

When building with `--shared-openssl`, Node.js requires OpenSSL 3.0 or later.
Support for building against OpenSSL 1.x was removed in Node.js 27.0.0, and
`configure` fails if an older version is detected.

It is the responsibility of any distribution
shipping with these options to:

Expand Down
20 changes: 14 additions & 6 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@
action='store_true',
dest='openssl_is_fips',
default=None,
help='specifies that the OpenSSL library is FIPS compatible')
help='specifies that the shared OpenSSL library is FIPS capable '
'(requires --shared-openssl)')

parser.add_argument('--openssl-use-def-ca-store',
action='store_true',
Expand Down Expand Up @@ -1523,7 +1524,7 @@ def get_openssl_version(o):

return version_number

except (OSError, ValueError, subprocess.SubprocessError) as e:
except (OSError, TypeError, ValueError, subprocess.SubprocessError) as e:
warn(f'Failed to determine OpenSSL version from header: {e}')
return 0

Expand Down Expand Up @@ -2275,7 +2276,6 @@ def configure_openssl(o):
variables['node_shared_ngtcp2'] = b(options.shared_ngtcp2)
variables['node_shared_nghttp3'] = b(options.shared_nghttp3)
variables['openssl_is_fips'] = b(options.openssl_is_fips)
variables['node_fipsinstall'] = b(False)

if options.openssl_no_asm:
variables['openssl_no_asm'] = 1
Expand Down Expand Up @@ -2330,17 +2330,25 @@ def without_ssl_error(option):
if options.openssl_no_asm and options.shared_openssl:
error('--openssl-no-asm is incompatible with --shared-openssl')

if options.openssl_is_fips and not options.shared_openssl:
error('--openssl-is-fips is only available with --shared-openssl')

if options.openssl_is_fips:
o['defines'] += ['OPENSSL_FIPS']

if options.openssl_is_fips and not options.shared_openssl:
variables['node_fipsinstall'] = b(True)

configure_library('openssl', o)

o['variables']['openssl_version'] = get_openssl_version(o)
o['variables']['openssl_is_boringssl'] = get_openssl_is_boringssl(o)

# BoringSSL identifies itself as OpenSSL 1.1.1 and is exempt from this check.
# A version of 0 means detection failed, which is already warned about in
# get_openssl_version() and is caught at compile time by ncrypto.h.
openssl_version = o['variables']['openssl_version']
if o['variables']['openssl_is_boringssl'] == 'false' and \
0 < openssl_version < 0x30000000:
error('OpenSSL 1.x is no longer supported, v3.0.0 or later is required.')

def configure_lief(o):
if options.without_lief:
if options.shared_lief:
Expand Down
5 changes: 2 additions & 3 deletions deps/ncrypto/engine.cc
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#include "ncrypto.h"

#if !defined(OPENSSL_NO_ENGINE) && \
((defined(NCRYPTO_ENGINE_COMPAT) && NCRYPTO_ENGINE_COMPAT) || \
NCRYPTO_USE_LEGACY_OPENSSL)
#if !defined(OPENSSL_NO_ENGINE) && defined(NCRYPTO_ENGINE_COMPAT) && \
NCRYPTO_ENGINE_COMPAT
#include <openssl/engine.h>
#endif

Expand Down
75 changes: 20 additions & 55 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#include <climits>
#include <cstring>
#include <string_view>
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
#include <openssl/core_names.h>
#include <openssl/params.h>
#include <openssl/provider.h>
Expand Down Expand Up @@ -59,17 +59,6 @@ constexpr static PQCMapping pqc_mappings[] = {

#endif

// EVP_PKEY_CTX_set_dsa_paramgen_q_bits was added in OpenSSL 1.1.1e.
#if OPENSSL_VERSION_NUMBER < 0x1010105fL
#define EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, qbits) \
EVP_PKEY_CTX_ctrl((ctx), \
EVP_PKEY_DSA, \
EVP_PKEY_OP_PARAMGEN, \
EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, \
(qbits), \
nullptr)
#endif

namespace ncrypto {
namespace {
using BignumCtxPointer = DeleteFnPtr<BN_CTX, BN_CTX_free>;
Expand Down Expand Up @@ -515,27 +504,26 @@ DataPointer DataPointer::resize(size_t len) {
// ============================================================================
bool isFipsEnabled() {
ClearErrorOnReturn clear_error_on_return;
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
return EVP_default_properties_is_fips_enabled(nullptr) == 1;
#else
return FIPS_mode() == 1;
#endif
}

bool setFipsEnabled(bool enable, CryptoErrorList* errors) {
if (isFipsEnabled() == enable) return true;
ClearErrorOnReturn clearErrorOnReturn(errors);
#if OPENSSL_VERSION_MAJOR >= 3
return EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1 &&
EVP_default_properties_is_fips_enabled(nullptr);
bool setFipsEnabled() {
if (isFipsEnabled()) return true;
ClearErrorOnReturn clearErrorOnReturn;
#ifndef OPENSSL_IS_BORINGSSL
return EVP_default_properties_enable_fips(nullptr, 1) == 1;
#else
return FIPS_mode_set(enable ? 1 : 0) == 1;
return FIPS_mode_set(1) == 1;
#endif
}

bool testFipsEnabled() {
ClearErrorOnReturn clear_error_on_return;
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
OSSL_PROVIDER* fips_provider = nullptr;
if (OSSL_PROVIDER_available(nullptr, "fips")) {
fips_provider = OSSL_PROVIDER_load(nullptr, "fips");
Expand Down Expand Up @@ -802,7 +790,7 @@ bool CSPRNG(void* buffer, size_t length) {
auto buf = reinterpret_cast<unsigned char*>(buffer);
do {
if (1 == RAND_status()) {
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
if (1 == RAND_bytes_ex(nullptr, buf, length, 0)) {
return true;
}
Expand All @@ -815,7 +803,7 @@ bool CSPRNG(void* buffer, size_t length) {
return true;
#endif
}
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
const auto code = ERR_peek_last_error();
// A misconfigured OpenSSL 3 installation may report 1 from RAND_poll()
// and RAND_status() but fail in RAND_bytes() if it cannot look up
Expand Down Expand Up @@ -1112,7 +1100,7 @@ bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) {
BIO_printf(out.get(), (j == 0) ? "%X" : ":%X", pair);
}
} else {
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
BIO_printf(out.get(), "<invalid length=%d>", ip_len);
#else
BIO_printf(out.get(), "<invalid>");
Expand All @@ -1131,9 +1119,9 @@ bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) {
// awkward, especially when passed to translatePeerCertificate.
bool unicode = true;
const char* prefix = nullptr;
// OpenSSL 1.1.1 does not support othername in GENERAL_NAME_print and may
// BoringSSL does not support othername in GENERAL_NAME_print and may
// not define these NIDs.
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
int nid = OBJ_obj2nid(gen->d.otherName->type_id);
switch (nid) {
case NID_id_on_SmtpUTF8Mailbox:
Expand All @@ -1153,7 +1141,7 @@ bool PrintGeneralName(const BIOPointer& out, const GENERAL_NAME* gen) {
prefix = "NAIRealm";
break;
}
#endif // OPENSSL_VERSION_MAJOR >= 3
#endif // !OPENSSL_IS_BORINGSSL
int val_type = gen->d.otherName->value->type;
if (prefix == nullptr || (unicode && val_type != V_ASN1_UTF8STRING) ||
(!unicode && val_type != V_ASN1_IA5STRING)) {
Expand Down Expand Up @@ -1244,7 +1232,7 @@ bool SafeX509InfoAccessPrint(const BIOPointer& out, const X509_EXTENSION* ext) {
}
sk_ACCESS_DESCRIPTION_pop_free(descs, ACCESS_DESCRIPTION_free);

#if OPENSSL_VERSION_MAJOR < 3
#ifdef OPENSSL_IS_BORINGSSL
BIO_write(out.get(), "\n", 1);
#endif

Expand Down Expand Up @@ -3679,12 +3667,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
cipher,
passphrase);
}
#else
#if OPENSSL_VERSION_MAJOR >= 3
const RSA* rsa = EVP_PKEY_get0_RSA(get());
#else
RSA* rsa = EVP_PKEY_get0_RSA(get());
#endif
switch (config.format) {
case PKFormatType::PEM: {
err = PEM_write_bio_RSAPrivateKey(
Expand Down Expand Up @@ -3754,12 +3738,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePrivateKey(
"type-specific",
cipher,
passphrase);
#else
#if OPENSSL_VERSION_MAJOR >= 3
const EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get());
#else
EC_KEY* ec = EVP_PKEY_get0_EC_KEY(get());
#endif
switch (config.format) {
case PKFormatType::PEM: {
err = PEM_write_bio_ECPrivateKey(
Expand Down Expand Up @@ -3820,12 +3800,8 @@ Result<BIOPointer, bool> EVPKeyPointer::writePublicKey(
mark_pop_error_on_return.peekError());
}
return bio;
#else
#if OPENSSL_VERSION_MAJOR >= 3
const RSA* rsa = EVP_PKEY_get0_RSA(get());
#else
RSA* rsa = EVP_PKEY_get0_RSA(get());
#endif
if (config.format == ncrypto::EVPKeyPointer::PKFormatType::PEM) {
// Encode PKCS#1 as PEM.
if (PEM_write_bio_RSAPublicKey(bio.get(), rsa) != 1) {
Expand Down Expand Up @@ -3953,14 +3929,7 @@ EVPKeyPointer::operator Rsa() const {
#if NCRYPTO_USE_OPENSSL3_PROVIDER
return Rsa(get());
#else
// TODO(tniessen): Remove the "else" branch once we drop support for OpenSSL
// versions older than 1.1.1e via FIPS / dynamic linking.
OSSL3_CONST RSA* rsa;
if (OPENSSL_VERSION_NUMBER >= 0x1010105fL) {
rsa = EVP_PKEY_get0_RSA(get());
} else {
rsa = static_cast<OSSL3_CONST RSA*>(EVP_PKEY_get0(get()));
}
OSSL3_CONST RSA* rsa = EVP_PKEY_get0_RSA(get());
if (rsa == nullptr) return {};
return Rsa(rsa);
#endif
Expand All @@ -3982,7 +3951,7 @@ EVPKeyPointer::operator Dsa() const {
bool EVPKeyPointer::validateDsaParameters() const {
if (!pkey_) return false;
/* Validate DSA2 parameters from FIPS 186-4 */
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
if (EVP_default_properties_is_fips_enabled(nullptr) && EVP_PKEY_DSA == id()) {
#else
if (FIPS_mode() && EVP_PKEY_DSA == id()) {
Expand Down Expand Up @@ -5391,11 +5360,7 @@ EVPKeyPointer EVPKeyCtxPointer::paramgen() const {
bool EVPKeyCtxPointer::publicCheck() const {
if (!ctx_) return false;
#ifndef OPENSSL_IS_BORINGSSL
#if OPENSSL_VERSION_MAJOR >= 3
return EVP_PKEY_public_check_quick(ctx_.get()) == 1;
#else
return EVP_PKEY_public_check(ctx_.get()) == 1;
#endif
#else // OPENSSL_IS_BORINGSSL
// Boringssl appears not to support this operation.
// TODO(jasnell): Is there an alternative approach that Boringssl does
Expand Down Expand Up @@ -5994,7 +5959,7 @@ struct CipherCallbackContext {
void operator()(const char* name) { cb(name); }
};

#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
template <class TypeName,
TypeName* fetch_type(OSSL_LIB_CTX*, const char*, const char*),
void free_type(TypeName*),
Expand Down Expand Up @@ -6050,7 +6015,7 @@ void Cipher::ForEach(Cipher::CipherNameCallback callback) {
}
#else
EVP_CIPHER_do_all_sorted(
#if OPENSSL_VERSION_MAJOR >= 3
#ifndef OPENSSL_IS_BORINGSSL
array_push_back<EVP_CIPHER,
EVP_CIPHER_fetch,
EVP_CIPHER_free,
Expand Down
Loading