diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc index 0a0da0263676d9..19444fc65d3882 100644 --- a/deps/ncrypto/ncrypto.cc +++ b/deps/ncrypto/ncrypto.cc @@ -111,20 +111,64 @@ DataPointer DataPointer::Alloc(size_t len) { #endif } +DataPointer DataPointer::SecureAlloc(size_t len) { +#ifndef OPENSSL_IS_BORINGSSL + auto ptr = OPENSSL_secure_zalloc(len); + if (ptr == nullptr) return {}; + return DataPointer(ptr, len, true); +#else + // BoringSSL does not implement the OPENSSL_secure_zalloc API. + auto ptr = OPENSSL_malloc(len); + if (ptr == nullptr) return {}; + memset(ptr, 0, len); + return DataPointer(ptr, len); +#endif +} + +size_t DataPointer::GetSecureHeapUsed() { +#ifndef OPENSSL_IS_BORINGSSL + return CRYPTO_secure_malloc_initialized() ? CRYPTO_secure_used() : 0; +#else + // BoringSSL does not have the secure heap and therefore + // will always return 0. + return 0; +#endif +} + +DataPointer::InitSecureHeapResult DataPointer::TryInitSecureHeap(size_t amount, + size_t min) { +#ifndef OPENSSL_IS_BORINGSSL + switch (CRYPTO_secure_malloc_init(amount, min)) { + case 0: + return InitSecureHeapResult::FAILED; + case 2: + return InitSecureHeapResult::UNABLE_TO_MEMORY_MAP; + case 1: + return InitSecureHeapResult::OK; + default: + return InitSecureHeapResult::FAILED; + } +#else + // BoringSSL does not actually support the secure heap + return InitSecureHeapResult::FAILED; +#endif +} + DataPointer DataPointer::Copy(const Buffer& buffer) { return DataPointer(OPENSSL_memdup(buffer.data, buffer.len), buffer.len); } -DataPointer::DataPointer(void* data, size_t length) - : data_(data), len_(length) {} +DataPointer::DataPointer(void* data, size_t length, bool secure) + : data_(data), len_(length), secure_(secure) {} -DataPointer::DataPointer(const Buffer& buffer) - : data_(buffer.data), len_(buffer.len) {} +DataPointer::DataPointer(const Buffer& buffer, bool secure) + : data_(buffer.data), len_(buffer.len), secure_(secure) {} DataPointer::DataPointer(DataPointer&& other) noexcept - : data_(other.data_), len_(other.len_) { + : data_(other.data_), len_(other.len_), secure_(other.secure_) { other.data_ = nullptr; other.len_ = 0; + other.secure_ = false; } DataPointer& DataPointer::operator=(DataPointer&& other) noexcept { @@ -144,7 +188,11 @@ void DataPointer::zero() { void DataPointer::reset(void* data, size_t length) { if (data_ != nullptr) { - OPENSSL_clear_free(data_, len_); + if (secure_) { + OPENSSL_secure_clear_free(data_, len_); + } else { + OPENSSL_clear_free(data_, len_); + } } data_ = data; len_ = length; @@ -175,6 +223,7 @@ DataPointer DataPointer::resize(size_t len) { // ============================================================================ bool isFipsEnabled() { + ClearErrorOnReturn clear_error_on_return; #if OPENSSL_VERSION_MAJOR >= 3 return EVP_default_properties_is_fips_enabled(nullptr) == 1; #else @@ -186,30 +235,31 @@ 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; + return EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1 && + EVP_default_properties_is_fips_enabled(nullptr); #else return FIPS_mode_set(enable ? 1 : 0) == 1; #endif } bool testFipsEnabled() { + ClearErrorOnReturn clear_error_on_return; #if OPENSSL_VERSION_MAJOR >= 3 OSSL_PROVIDER* fips_provider = nullptr; if (OSSL_PROVIDER_available(nullptr, "fips")) { fips_provider = OSSL_PROVIDER_load(nullptr, "fips"); } - const auto enabled = fips_provider == nullptr ? 0 - : OSSL_PROVIDER_self_test(fips_provider) ? 1 - : 0; + if (fips_provider == nullptr) return false; + int result = OSSL_PROVIDER_self_test(fips_provider); + OSSL_PROVIDER_unload(fips_provider); + return result; #else #ifdef OPENSSL_FIPS - const auto enabled = FIPS_selftest() ? 1 : 0; + return FIPS_selftest(); #else // OPENSSL_FIPS - const auto enabled = 0; + return false; #endif // OPENSSL_FIPS #endif - - return enabled; } // ============================================================================ @@ -2685,6 +2735,21 @@ std::optional SSLPointer::getCipherVersion() const { return SSL_CIPHER_get_version(cipher); } +std::optional SSLPointer::getSecurityLevel() { +#ifndef OPENSSL_IS_BORINGSSL + auto ctx = SSLCtxPointer::New(); + if (!ctx) return std::nullopt; + + auto ssl = SSLPointer::New(ctx); + if (!ssl) return std::nullopt; + + return SSL_get_security_level(ssl); +#else + // for BoringSSL assume the same as the default + return OPENSSL_TLS_SECURITY_LEVEL; +#endif // OPENSSL_IS_BORINGSSL +} + SSLCtxPointer::SSLCtxPointer(SSL_CTX* ctx) : ctx_(ctx) {} SSLCtxPointer::SSLCtxPointer(SSLCtxPointer&& other) noexcept @@ -3198,6 +3263,10 @@ EVPKeyCtxPointer EVPKeyCtxPointer::New(const EVPKeyPointer& key) { } EVPKeyCtxPointer EVPKeyCtxPointer::NewFromID(int id) { +#ifdef OPENSSL_IS_BORINGSSL + // DSA keys are not supported with BoringSSL + if (id == EVP_PKEY_DSA) return {}; +#endif return EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(id, nullptr)); } @@ -3687,6 +3756,26 @@ int Ec::getCurve() const { return EC_GROUP_get_curve_name(getGroup()); } +int Ec::GetCurveIdFromName(std::string_view name) { + int nid = EC_curve_nist2nid(name.data()); + if (nid == NID_undef) { + nid = OBJ_sn2nid(name.data()); + } + return nid; +} + +bool Ec::GetCurves(Ec::GetCurveCallback callback) { + const size_t count = EC_get_builtin_curves(nullptr, 0); + std::vector curves(count); + if (EC_get_builtin_curves(curves.data(), count) != count) { + return false; + } + for (auto curve : curves) { + if (!callback(OBJ_nid2sn(curve.nid))) return false; + } + return true; +} + // ============================================================================ EVPMDCtxPointer::EVPMDCtxPointer() : ctx_(nullptr) {} diff --git a/deps/ncrypto/ncrypto.h b/deps/ncrypto/ncrypto.h index f73c84edce20bf..6b17012abf2ebc 100644 --- a/deps/ncrypto/ncrypto.h +++ b/deps/ncrypto/ncrypto.h @@ -397,6 +397,11 @@ class Ec final { inline operator bool() const { return ec_ != nullptr; } inline operator OSSL3_CONST EC_KEY*() const { return ec_; } + static int GetCurveIdFromName(std::string_view name); + + using GetCurveCallback = std::function; + static bool GetCurves(GetCurveCallback callback); + private: OSSL3_CONST EC_KEY* ec_ = nullptr; }; @@ -408,9 +413,31 @@ class DataPointer final { static DataPointer Alloc(size_t len); static DataPointer Copy(const Buffer& buffer); + // Attempts to allocate the buffer space using the secure heap, if + // supported/enabled. If the secure heap is disabled, then this + // ends up being equivalent to Alloc(len). Note that allocation + // will fail if there is not enough free space remaining in the + // secure heap space. + static DataPointer SecureAlloc(size_t len); + + // If the secure heap is enabled, returns the amount of data that + // has been allocated from the heap. + static size_t GetSecureHeapUsed(); + + enum class InitSecureHeapResult { + FAILED, + UNABLE_TO_MEMORY_MAP, + OK, + }; + + // Attempt to initialize the secure heap. The secure heap is not + // supported on all operating systems and whenever boringssl is + // used. + static InitSecureHeapResult TryInitSecureHeap(size_t amount, size_t min); + DataPointer() = default; - explicit DataPointer(void* data, size_t len); - explicit DataPointer(const Buffer& buffer); + explicit DataPointer(void* data, size_t len, bool secure = false); + explicit DataPointer(const Buffer& buffer, bool secure = false); DataPointer(DataPointer&& other) noexcept; DataPointer& operator=(DataPointer&& other) noexcept; NCRYPTO_DISALLOW_COPY(DataPointer) @@ -446,9 +473,12 @@ class DataPointer final { }; } + bool isSecure() const { return secure_; } + private: void* data_ = nullptr; size_t len_ = 0; + bool secure_ = false; }; class BIOPointer final { @@ -968,6 +998,8 @@ class SSLPointer final { std::optional verifyPeerCertificate() const; + static std::optional getSecurityLevel(); + void getCiphers(std::function cb) const; static SSLPointer New(const SSLCtxPointer& ctx); diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc index 1bf91b37e8b3a6..85996f605a9c52 100644 --- a/src/crypto/crypto_cipher.cc +++ b/src/crypto/crypto_cipher.cc @@ -1005,15 +1005,11 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo& args) { return ThrowCryptoError(env, ERR_get_error()); } -#ifndef OPENSSL_IS_BORINGSSL - // RSA implicit rejection here is not supported by BoringSSL. - // Skip this check when boring is used. if (!ctx.setRsaImplicitRejection()) { return THROW_ERR_INVALID_ARG_VALUE( env, "RSA_PKCS1_PADDING is no longer supported for private decryption"); } -#endif } const EVP_MD* digest = nullptr; diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc index e81bda7ad36f2b..2b55dcb1119820 100644 --- a/src/crypto/crypto_common.cc +++ b/src/crypto/crypto_common.cc @@ -243,11 +243,10 @@ MaybeLocal GetEphemeralKey(Environment* env, const SSLPointer& ssl) { MaybeLocal ECPointToBuffer(Environment* env, const EC_GROUP* group, const EC_POINT* point, - point_conversion_form_t form, - const char** error) { + point_conversion_form_t form) { size_t len = EC_POINT_point2oct(group, point, form, nullptr, 0, nullptr); if (len == 0) { - if (error != nullptr) *error = "Failed to get public key length"; + THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get public key length"); return MaybeLocal(); } @@ -261,7 +260,7 @@ MaybeLocal ECPointToBuffer(Environment* env, bs->ByteLength(), nullptr); if (len == 0) { - if (error != nullptr) *error = "Failed to get public key"; + THROW_ERR_CRYPTO_OPERATION_FAILED(env, "Failed to get public key"); return MaybeLocal(); } diff --git a/src/crypto/crypto_common.h b/src/crypto/crypto_common.h index 8bd561b9e1b57e..9254b554d063ee 100644 --- a/src/crypto/crypto_common.h +++ b/src/crypto/crypto_common.h @@ -35,12 +35,10 @@ v8::MaybeLocal GetPeerCert(Environment* env, bool abbreviated = false, bool is_server = false); -v8::MaybeLocal ECPointToBuffer( - Environment* env, - const EC_GROUP* group, - const EC_POINT* point, - point_conversion_form_t form, - const char** error); +v8::MaybeLocal ECPointToBuffer(Environment* env, + const EC_GROUP* group, + const EC_POINT* point, + point_conversion_form_t form); } // namespace crypto } // namespace node diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc index fb794792aae521..57f2a0db772c84 100644 --- a/src/crypto/crypto_dh.cc +++ b/src/crypto/crypto_dh.cc @@ -55,11 +55,17 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const { namespace { MaybeLocal DataPointerToBuffer(Environment* env, DataPointer&& data) { + struct Flag { + bool secure; + }; auto backing = ArrayBuffer::NewBackingStore( data.get(), data.size(), - [](void* data, size_t len, void* ptr) { DataPointer free_me(data, len); }, - nullptr); + [](void* data, size_t len, void* ptr) { + std::unique_ptr flag(static_cast(ptr)); + DataPointer free_me(data, len, flag->secure); + }, + new Flag{data.isSecure()}); data.release(); auto ab = ArrayBuffer::New(env->isolate(), std::move(backing)); @@ -482,6 +488,7 @@ ByteSource StatelessDiffieHellmanThreadsafe(const EVPKeyPointer& our_key, const EVPKeyPointer& their_key) { auto dp = DHPointer::stateless(our_key, their_key); if (!dp) return {}; + DCHECK(!dp.isSecure()); return ByteSource::Allocated(dp.release()); } diff --git a/src/crypto/crypto_dsa.cc b/src/crypto/crypto_dsa.cc index 8aacb995f78989..eff34444999fbb 100644 --- a/src/crypto/crypto_dsa.cc +++ b/src/crypto/crypto_dsa.cc @@ -28,13 +28,9 @@ using v8::Value; namespace crypto { EVPKeyCtxPointer DsaKeyGenTraits::Setup(DsaKeyPairGenConfig* params) { -#ifdef OPENSSL_IS_BORINGSSL - // Operation is unsupported in BoringSSL - return {}; -#else auto param_ctx = EVPKeyCtxPointer::NewFromID(EVP_PKEY_DSA); - if (!param_ctx.initForParamgen() || + if (!param_ctx || !param_ctx.initForParamgen() || !param_ctx.setDsaParameters( params->params.modulus_bits, params->params.divisor_bits != -1 @@ -49,7 +45,6 @@ EVPKeyCtxPointer DsaKeyGenTraits::Setup(DsaKeyPairGenConfig* params) { EVPKeyCtxPointer key_ctx = key_params.newCtx(); if (!key_ctx.initForKeygen()) return {}; return key_ctx; -#endif } // Input arguments for DsaKeyPairGenJob diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc index f30d008ab178ce..6c4ef342425a74 100644 --- a/src/crypto/crypto_ec.cc +++ b/src/crypto/crypto_ec.cc @@ -20,6 +20,7 @@ namespace node { using ncrypto::BignumPointer; using ncrypto::DataPointer; +using ncrypto::Ec; using ncrypto::ECGroupPointer; using ncrypto::ECKeyPointer; using ncrypto::ECPointPointer; @@ -47,13 +48,6 @@ using v8::Value; namespace crypto { -int GetCurveFromName(const char* name) { - int nid = EC_curve_nist2nid(name); - if (nid == NID_undef) - nid = OBJ_sn2nid(name); - return nid; -} - void ECDH::Initialize(Environment* env, Local target) { Isolate* isolate = env->isolate(); Local context = env->context(); @@ -100,13 +94,10 @@ void ECDH::RegisterExternalReferences(ExternalReferenceRegistry* registry) { void ECDH::GetCurves(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - const size_t num_curves = EC_get_builtin_curves(nullptr, 0); - std::vector curves(num_curves); - CHECK_EQ(EC_get_builtin_curves(curves.data(), num_curves), num_curves); - - LocalVector arr(env->isolate(), num_curves); - std::transform(curves.begin(), curves.end(), arr.begin(), [env](auto& curve) { - return OneByteString(env->isolate(), OBJ_nid2sn(curve.nid)); + LocalVector arr(env->isolate()); + Ec::GetCurves([&](std::string_view curve) -> bool { + arr.push_back(OneByteString(env->isolate(), curve)); + return true; }); args.GetReturnValue().Set(Array::New(env->isolate(), arr.data(), arr.size())); } @@ -235,11 +226,10 @@ void ECDH::GetPublicKey(const FunctionCallbackInfo& args) { uint32_t val = args[0].As()->Value(); point_conversion_form_t form = static_cast(val); - const char* error; Local buf; - if (!ECPointToBuffer(env, group, pub, form, &error).ToLocal(&buf)) - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, error); - args.GetReturnValue().Set(buf); + if (ECPointToBuffer(env, group, pub, form).ToLocal(&buf)) { + args.GetReturnValue().Set(buf); + } } void ECDH::GetPrivateKey(const FunctionCallbackInfo& args) { @@ -397,11 +387,10 @@ void ECDH::ConvertKey(const FunctionCallbackInfo& args) { uint32_t val = args[2].As()->Value(); point_conversion_form_t form = static_cast(val); - const char* error; Local buf; - if (!ECPointToBuffer(env, group, pub, form, &error).ToLocal(&buf)) - return THROW_ERR_CRYPTO_OPERATION_FAILED(env, error); - args.GetReturnValue().Set(buf); + if (ECPointToBuffer(env, group, pub, form).ToLocal(&buf)) { + args.GetReturnValue().Set(buf); + } } void ECDHBitsConfig::MemoryInfo(MemoryTracker* tracker) const { @@ -460,6 +449,7 @@ bool ECDHBitsTraits::DeriveBits(Environment* env, auto data = ctx.derive(); if (!data) return false; + DCHECK(!data.isSecure()); *out = ByteSource::Allocated(data.release()); break; @@ -550,7 +540,7 @@ Maybe EcKeyGenTraits::AdditionalConfig( CHECK(args[*offset + 1]->IsInt32()); // param encoding Utf8Value curve_name(env->isolate(), args[*offset]); - params->params.curve_nid = GetCurveFromName(*curve_name); + params->params.curve_nid = Ec::GetCurveIdFromName(curve_name.ToStringView()); if (params->params.curve_nid == NID_undef) { THROW_ERR_CRYPTO_INVALID_CURVE(env); return Nothing(); @@ -583,12 +573,14 @@ WebCryptoKeyExportStatus EC_Raw_Export(const KeyObjectData& key_data, case kKeyTypePrivate: { auto data = m_pkey.rawPrivateKey(); if (!data) return WebCryptoKeyExportStatus::INVALID_KEY_TYPE; + DCHECK(!data.isSecure()); *out = ByteSource::Allocated(data.release()); break; } case kKeyTypePublic: { auto data = m_pkey.rawPublicKey(); if (!data) return WebCryptoKeyExportStatus::INVALID_KEY_TYPE; + DCHECK(!data.isSecure()); *out = ByteSource::Allocated(data.release()); break; } @@ -841,7 +833,7 @@ KeyObjectData ImportJWKEcKey(Environment* env, CHECK(args[offset]->IsString()); // curve name Utf8Value curve(env->isolate(), args[offset].As()); - int nid = GetCurveFromName(*curve); + int nid = Ec::GetCurveIdFromName(curve.ToStringView()); if (nid == NID_undef) { // Unknown curve THROW_ERR_CRYPTO_INVALID_CURVE(env); return {}; diff --git a/src/crypto/crypto_ec.h b/src/crypto/crypto_ec.h index 308f0b6d0440ab..640d9b26f3b15f 100644 --- a/src/crypto/crypto_ec.h +++ b/src/crypto/crypto_ec.h @@ -15,7 +15,6 @@ namespace node { namespace crypto { -int GetCurveFromName(const char* name); class ECDH final : public BaseObject { public: diff --git a/src/crypto/crypto_hash.cc b/src/crypto/crypto_hash.cc index 851847483327c1..52cda485aaa8da 100644 --- a/src/crypto/crypto_hash.cc +++ b/src/crypto/crypto_hash.cc @@ -405,6 +405,7 @@ void Hash::HashDigest(const FunctionCallbackInfo& args) { if (!data) [[unlikely]] { return ThrowCryptoError(env, ERR_get_error()); } + DCHECK(!data.isSecure()); hash->digest_ = ByteSource::Allocated(data.release()); } @@ -504,6 +505,7 @@ bool HashTraits::DeriveBits( if (!data) [[unlikely]] return false; + DCHECK(!data.isSecure()); *out = ByteSource::Allocated(data.release()); } diff --git a/src/crypto/crypto_hkdf.cc b/src/crypto/crypto_hkdf.cc index 10bb8e4258bf63..541c526fb2f646 100644 --- a/src/crypto/crypto_hkdf.cc +++ b/src/crypto/crypto_hkdf.cc @@ -117,6 +117,7 @@ bool HKDFTraits::DeriveBits( params.length); if (!dp) return false; + DCHECK(!dp.isSecure()); *out = ByteSource::Allocated(dp.release()); return true; } diff --git a/src/crypto/crypto_hmac.cc b/src/crypto/crypto_hmac.cc index 56a09e1a2d9b0f..ee06458b574767 100644 --- a/src/crypto/crypto_hmac.cc +++ b/src/crypto/crypto_hmac.cc @@ -258,6 +258,7 @@ bool HmacTraits::DeriveBits( if (!buf) [[unlikely]] return false; + DCHECK(!buf.isSecure()); *out = ByteSource::Allocated(buf.release()); return true; diff --git a/src/crypto/crypto_keys.cc b/src/crypto/crypto_keys.cc index 3016f9510ef3f0..db002875d2657b 100644 --- a/src/crypto/crypto_keys.cc +++ b/src/crypto/crypto_keys.cc @@ -924,10 +924,6 @@ void KeyObjectHandle::GetAsymmetricKeyType( } bool KeyObjectHandle::CheckEcKeyData() const { -#ifdef OPENSSL_IS_BORINGSSL - // Operation is unsupported on BoringSSL - return true; -#else MarkPopErrorOnReturn mark_pop_error_on_return; const auto& key = data_.GetAsymmetricKey(); @@ -937,7 +933,6 @@ bool KeyObjectHandle::CheckEcKeyData() const { return data_.GetKeyType() == kKeyTypePrivate ? ctx.privateCheck() : ctx.publicCheck(); -#endif } void KeyObjectHandle::CheckEcKeyData(const FunctionCallbackInfo& args) { diff --git a/src/crypto/crypto_pbkdf2.cc b/src/crypto/crypto_pbkdf2.cc index 1a0dff8238d938..f85dbcc31a5201 100644 --- a/src/crypto/crypto_pbkdf2.cc +++ b/src/crypto/crypto_pbkdf2.cc @@ -126,6 +126,7 @@ bool PBKDF2Traits::DeriveBits(Environment* env, params.length); if (!dp) return false; + DCHECK(!dp.isSecure()); *out = ByteSource::Allocated(dp.release()); return true; } diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc index 7742cf204df6d8..be1748be24a657 100644 --- a/src/crypto/crypto_rsa.cc +++ b/src/crypto/crypto_rsa.cc @@ -204,6 +204,7 @@ WebCryptoCipherStatus RSA_Cipher(Environment* env, auto data = cipher(m_pkey, nparams, in); if (!data) return WebCryptoCipherStatus::FAILED; + DCHECK(!data.isSecure()); *out = ByteSource::Allocated(data.release()); return WebCryptoCipherStatus::OK; diff --git a/src/crypto/crypto_scrypt.cc b/src/crypto/crypto_scrypt.cc index 1886543e6e01cb..f6418644fedf86 100644 --- a/src/crypto/crypto_scrypt.cc +++ b/src/crypto/crypto_scrypt.cc @@ -140,6 +140,7 @@ bool ScryptTraits::DeriveBits( params.length); if (!dp) return false; + DCHECK(!dp.isSecure()); *out = ByteSource::Allocated(dp.release()); return true; } diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc index a28f12237d0883..ae8d718036e34e 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -682,6 +682,7 @@ bool SignTraits::DeriveBits( crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } + DCHECK(!data.isSecure()); *out = ByteSource::Allocated(data.release()); } else { auto data = context.sign(params.data); @@ -689,6 +690,7 @@ bool SignTraits::DeriveBits( crypto::CheckThrow(env, SignBase::Error::PrivateKey); return false; } + DCHECK(!data.isSecure()); auto bs = ByteSource::Allocated(data.release()); if (UseP1363Encoding(key, params.dsa_encoding)) { diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc index 737149b83dda4c..55cd7382eb7802 100644 --- a/src/crypto/crypto_util.cc +++ b/src/crypto/crypto_util.cc @@ -22,8 +22,6 @@ #include "openssl/provider.h" #endif -#include - namespace node { using ncrypto::BignumPointer; @@ -33,8 +31,6 @@ using ncrypto::DataPointer; #ifndef OPENSSL_NO_ENGINE using ncrypto::EnginePointer; #endif // !OPENSSL_NO_ENGINE -using ncrypto::EVPKeyCtxPointer; -using ncrypto::SSLCtxPointer; using ncrypto::SSLPointer; using v8::ArrayBuffer; using v8::BackingStore; @@ -88,16 +84,12 @@ bool ProcessFipsOptions() { if (per_process::cli_options->enable_fips_crypto || per_process::cli_options->force_fips_crypto) { #if OPENSSL_VERSION_MAJOR >= 3 - OSSL_PROVIDER* fips_provider = OSSL_PROVIDER_load(nullptr, "fips"); - if (fips_provider == nullptr) - return false; - OSSL_PROVIDER_unload(fips_provider); - - return EVP_default_properties_enable_fips(nullptr, 1) && - EVP_default_properties_is_fips_enabled(nullptr); + if (!ncrypto::testFipsEnabled()) return false; + return ncrypto::setFipsEnabled(true, nullptr); #else + // TODO(@jasnell): Remove this ifdef branch when openssl 1.1.1 is + // no longer supported. if (FIPS_mode() == 0) return FIPS_mode_set(1); - #endif } return true; @@ -148,17 +140,17 @@ void InitCryptoOnce() { #ifndef _WIN32 if (per_process::cli_options->secure_heap != 0) { - switch (CRYPTO_secure_malloc_init( - per_process::cli_options->secure_heap, - static_cast(per_process::cli_options->secure_heap_min))) { - case 0: + switch (DataPointer::TryInitSecureHeap( + per_process::cli_options->secure_heap, + per_process::cli_options->secure_heap_min)) { + case DataPointer::InitSecureHeapResult::FAILED: fprintf(stderr, "Unable to initialize openssl secure heap.\n"); break; - case 2: + case DataPointer::InitSecureHeapResult::UNABLE_TO_MEMORY_MAP: // Not a fatal error but worthy of a warning. fprintf(stderr, "Unable to memory map openssl secure heap.\n"); break; - case 1: + case DataPointer::InitSecureHeapResult::OK: // OK! break; } @@ -207,24 +199,13 @@ void TestFipsCrypto(const v8::FunctionCallbackInfo& args) { } void GetOpenSSLSecLevelCrypto(const FunctionCallbackInfo& args) { - // for BoringSSL assume the same as the default - int sec_level = OPENSSL_TLS_SECURITY_LEVEL; -#ifndef OPENSSL_IS_BORINGSSL - Environment* env = Environment::GetCurrent(args); - - auto ctx = SSLCtxPointer::New(); - if (!ctx) { - return ThrowCryptoError(env, ERR_get_error(), "SSL_CTX_new"); - } - - auto ssl = SSLPointer::New(ctx); - if (!ssl) { - return ThrowCryptoError(env, ERR_get_error(), "SSL_new"); + ncrypto::ClearErrorOnReturn clear_error_on_return; + if (auto sec_level = SSLPointer::getSecurityLevel()) { + return args.GetReturnValue().Set(sec_level.value()); } - - sec_level = SSL_get_security_level(ssl); -#endif // OPENSSL_IS_BORINGSSL - args.GetReturnValue().Set(sec_level); + Environment* env = Environment::GetCurrent(args); + ThrowCryptoError( + env, clear_error_on_return.peekError(), "getOpenSSLSecLevel"); } void CryptoErrorStore::Capture() { @@ -308,16 +289,14 @@ MaybeLocal CryptoErrorStore::ToException( // Use last element as the error message, everything else goes // into the .opensslErrorStack property on the exception object. const std::string& last_error_string = copy.errors_.back(); - Local exception_string; - if (!String::NewFromUtf8( - env->isolate(), - last_error_string.data(), - NewStringType::kNormal, - last_error_string.size()).ToLocal(&exception_string)) { + Local exception_string; + if (!ToV8Value(env->context(), last_error_string) + .ToLocal(&exception_string)) { return MaybeLocal(); } + DCHECK(exception_string->IsString()); copy.errors_.pop_back(); - return copy.ToException(env, exception_string); + return copy.ToException(env, exception_string.As()); } Local exception_v = Exception::Error(exception_string); @@ -625,18 +604,22 @@ void SetEngine(const FunctionCallbackInfo& args) { } #endif // !OPENSSL_NO_ENGINE -MaybeLocal EncodeBignum( - Environment* env, - const BIGNUM* bn, - int size, - Local* error) { +MaybeLocal EncodeBignum(Environment* env, const BIGNUM* bn, int size) { auto buf = BignumPointer::EncodePadded(bn, size); CHECK_EQ(buf.size(), static_cast(size)); - return StringBytes::Encode(env->isolate(), - reinterpret_cast(buf.get()), - buf.size(), - BASE64URL, - error); + Local ret; + Local error; + if (!StringBytes::Encode(env->isolate(), + reinterpret_cast(buf.get()), + buf.size(), + BASE64URL, + &error) + .ToLocal(&ret)) { + CHECK(!error.IsEmpty()); + env->isolate()->ThrowException(error); + return MaybeLocal(); + } + return ret; } Maybe SetEncodedValue(Environment* env, @@ -645,28 +628,15 @@ Maybe SetEncodedValue(Environment* env, const BIGNUM* bn, int size) { Local value; - Local error; CHECK_NOT_NULL(bn); if (size == 0) size = BignumPointer::GetByteCount(bn); - if (!EncodeBignum(env, bn, size, &error).ToLocal(&value)) { - if (!error.IsEmpty()) - env->isolate()->ThrowException(error); + if (!EncodeBignum(env, bn, size).ToLocal(&value)) { return Nothing(); } return target->Set(env->context(), name, value).IsJust() ? JustVoid() : Nothing(); } -bool SetRsaOaepLabel(EVPKeyCtxPointer* ctx, const ByteSource& label) { - if (label.size() != 0) { - // OpenSSL takes ownership of the label, so we need to create a copy. - auto dup = ncrypto::DataPointer::Copy(label); - if (!dup) return false; - return ctx->setRsaOaepLabel(std::move(dup)); - } - return true; -} - CryptoJobMode GetCryptoJobMode(v8::Local args) { CHECK(args->IsUint32()); uint32_t mode = args.As()->Value(); @@ -675,8 +645,8 @@ CryptoJobMode GetCryptoJobMode(v8::Local args) { } namespace { -// SecureBuffer uses OPENSSL_secure_malloc to allocate a Uint8Array. -// Without --secure-heap, OpenSSL's secure heap is disabled, +// SecureBuffer uses OpenSSL's secure heap feature to allocate a +// Uint8Array. Without --secure-heap, OpenSSL's secure heap is disabled, // in which case this has the same semantics as // using OPENSSL_malloc. However, if the secure heap is // initialized, SecureBuffer will automatically use it. @@ -684,46 +654,37 @@ void SecureBuffer(const FunctionCallbackInfo& args) { CHECK(args[0]->IsUint32()); Environment* env = Environment::GetCurrent(args); uint32_t len = args[0].As()->Value(); -#ifndef OPENSSL_IS_BORINGSSL - void* data = OPENSSL_secure_zalloc(len); -#else - void* data = OPENSSL_malloc(len); -#endif - if (data == nullptr) { - // There's no memory available for the allocation. - // Return nothing. - return; + + auto data = DataPointer::SecureAlloc(len); + CHECK(data.isSecure()); + if (!data) { + return THROW_ERR_OPERATION_FAILED(env, "Allocation failed"); } -#ifdef OPENSSL_IS_BORINGSSL - memset(data, 0, len); -#endif - std::shared_ptr store = - ArrayBuffer::NewBackingStore( - data, - len, - [](void* data, size_t len, void* deleter_data) { -#ifndef OPENSSL_IS_BORINGSSL - OPENSSL_secure_clear_free(data, len); -#else - OPENSSL_clear_free(data, len); -#endif - }, - data); + auto released = data.release(); + + std::shared_ptr store = ArrayBuffer::NewBackingStore( + released.data, + released.len, + [](void* data, size_t len, void* deleter_data) { + // The DataPointer takes ownership and will appropriately + // free the data when it gets reset. + DataPointer free_me( + ncrypto::Buffer{ + .data = data, + .len = len, + }, + true); + }, + nullptr); + Local buffer = ArrayBuffer::New(env->isolate(), store); args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len)); } void SecureHeapUsed(const FunctionCallbackInfo& args) { -#ifndef OPENSSL_IS_BORINGSSL Environment* env = Environment::GetCurrent(args); - if (CRYPTO_secure_malloc_initialized()) - args.GetReturnValue().Set( - BigInt::New(env->isolate(), CRYPTO_secure_used())); -#else - // BoringSSL does not have the secure heap and therefore - // will always return 0. - args.GetReturnValue().Set(BigInt::New(args.GetIsolate(), 0)); -#endif + args.GetReturnValue().Set( + BigInt::New(env->isolate(), DataPointer::GetSecureHeapUsed())); } } // namespace @@ -742,7 +703,7 @@ void Initialize(Environment* env, Local target) { NODE_DEFINE_CONSTANT(target, kCryptoJobSync); SetMethod(context, target, "secureBuffer", SecureBuffer); - SetMethod(context, target, "secureHeapUsed", SecureHeapUsed); + SetMethodNoSideEffect(context, target, "secureHeapUsed", SecureHeapUsed); SetMethodNoSideEffect( context, target, "getOpenSSLSecLevelCrypto", GetOpenSSLSecLevelCrypto); diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 8015e1ab00dab6..ac6ee0cfc6a225 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -14,21 +14,6 @@ #include "ncrypto.h" -#include -#include -#include -#include -#include -#include -#include -#include - -// The FIPS-related functions are only available -// when the OpenSSL itself was compiled with FIPS support. -#if defined(OPENSSL_FIPS) && OPENSSL_VERSION_MAJOR < 3 -# include -#endif // OPENSSL_FIPS - #include #include #include @@ -37,9 +22,7 @@ #include #include -namespace node { - -namespace crypto { +namespace node::crypto { // Currently known sizes of commonly used OpenSSL struct sizes. // OpenSSL considers it's various structs to be opaque and the // sizes may change from one version of OpenSSL to another, so @@ -651,11 +634,9 @@ class ArrayBufferOrViewContents final { void operator delete[](void*); }; -v8::MaybeLocal EncodeBignum( - Environment* env, - const BIGNUM* bn, - int size, - v8::Local* error); +v8::MaybeLocal EncodeBignum(Environment* env, + const BIGNUM* bn, + int size); v8::Maybe SetEncodedValue(Environment* env, v8::Local target, @@ -663,15 +644,11 @@ v8::Maybe SetEncodedValue(Environment* env, const BIGNUM* bn, int size = 0); -bool SetRsaOaepLabel(ncrypto::EVPKeyCtxPointer* rsa, const ByteSource& label); - namespace Util { void Initialize(Environment* env, v8::Local target); void RegisterExternalReferences(ExternalReferenceRegistry* registry); } // namespace Util - -} // namespace crypto -} // namespace node +} // namespace node::crypto #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #endif // SRC_CRYPTO_CRYPTO_UTIL_H_ diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc index b974667a4cacb9..5d550b8c632895 100644 --- a/src/crypto/crypto_x509.cc +++ b/src/crypto/crypto_x509.cc @@ -650,7 +650,7 @@ MaybeLocal GetECPubKey(Environment* env, if (pubkey == nullptr) [[unlikely]] return Undefined(env->isolate()); - return ECPointToBuffer(env, group, pubkey, EC_KEY_get_conv_form(ec), nullptr) + return ECPointToBuffer(env, group, pubkey, EC_KEY_get_conv_form(ec)) .FromMaybe(Local()); }