Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: cleanup crypto more #57323

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
117 changes: 103 additions & 14 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<const void>& 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<void>& buffer)
: data_(buffer.data), len_(buffer.len) {}
DataPointer::DataPointer(const Buffer<void>& 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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

// ============================================================================
Expand Down Expand Up @@ -2685,6 +2735,21 @@ std::optional<std::string_view> SSLPointer::getCipherVersion() const {
return SSL_CIPHER_get_version(cipher);
}

std::optional<int> 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
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -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<EC_builtin_curve> 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) {}
Expand Down
36 changes: 34 additions & 2 deletions deps/ncrypto/ncrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool(std::string_view)>;
static bool GetCurves(GetCurveCallback callback);

private:
OSSL3_CONST EC_KEY* ec_ = nullptr;
};
Expand All @@ -408,9 +413,31 @@ class DataPointer final {
static DataPointer Alloc(size_t len);
static DataPointer Copy(const Buffer<const void>& 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<void>& buffer);
explicit DataPointer(void* data, size_t len, bool secure = false);
explicit DataPointer(const Buffer<void>& buffer, bool secure = false);
DataPointer(DataPointer&& other) noexcept;
DataPointer& operator=(DataPointer&& other) noexcept;
NCRYPTO_DISALLOW_COPY(DataPointer)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -968,6 +998,8 @@ class SSLPointer final {

std::optional<uint32_t> verifyPeerCertificate() const;

static std::optional<int> getSecurityLevel();

void getCiphers(std::function<void(const std::string_view)> cb) const;

static SSLPointer New(const SSLCtxPointer& ctx);
Expand Down
4 changes: 0 additions & 4 deletions src/crypto/crypto_cipher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1005,15 +1005,11 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& 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;
Expand Down
7 changes: 3 additions & 4 deletions src/crypto/crypto_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,10 @@ MaybeLocal<Object> GetEphemeralKey(Environment* env, const SSLPointer& ssl) {
MaybeLocal<Object> 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<Object>();
}

Expand All @@ -261,7 +260,7 @@ MaybeLocal<Object> 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<Object>();
}

Expand Down
10 changes: 4 additions & 6 deletions src/crypto/crypto_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,10 @@ v8::MaybeLocal<v8::Value> GetPeerCert(Environment* env,
bool abbreviated = false,
bool is_server = false);

v8::MaybeLocal<v8::Object> ECPointToBuffer(
Environment* env,
const EC_GROUP* group,
const EC_POINT* point,
point_conversion_form_t form,
const char** error);
v8::MaybeLocal<v8::Object> ECPointToBuffer(Environment* env,
const EC_GROUP* group,
const EC_POINT* point,
point_conversion_form_t form);

} // namespace crypto
} // namespace node
Expand Down
11 changes: 9 additions & 2 deletions src/crypto/crypto_dh.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,17 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {

namespace {
MaybeLocal<Value> 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> flag(static_cast<Flag*>(ptr));
DataPointer free_me(data, len, flag->secure);
},
new Flag{data.isSecure()});
data.release();

auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
Expand Down Expand Up @@ -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());
}
Expand Down
7 changes: 1 addition & 6 deletions src/crypto/crypto_dsa.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading
Loading