Skip to content

Commit d0d300f

Browse files
panvaaduh95
authored andcommitted
crypto: discover hashes from OpenSSL providers
Enumerate usable digests and aliases from activated OpenSSL 3 providers rather than relying only on the legacy digest registry. Normalize provider aliases, omit numeric OIDs and NULL, and validate them against the active default property query. Preserve legacy names and the OpenSSL 1.1.1 and BoringSSL paths. Expose KECCAK-KMAC-128, KECCAK-256, SHA256-192, and other provider digests. Add `functionName` and `customization` options for cSHAKE digests in `createHash()` and `crypto.hash()` with OpenSSL 4.0 or later. Resolve provider-only digest names across hashing, HMAC, KDF, signing, verification, and RSA digest options. Keep ordinary hash construction and one-shot hashing on the original binding arities and direct initialization paths. Use parameterized setup only when cSHAKE options are supplied. Lazily cache successful provider fetches per Environment. Index entries by case-insensitive query, canonical, and alias names. Deduplicate owners by provider and canonical identity. Return borrowed pointers on warm hits. Introduce a process-wide FIPS-state generation that advances only after successful, state-changing `setFips()` calls. Use it to invalidate per-Environment digest caches and refresh `getHashes()` snapshots in the main thread and workers. Keep cache IDs monotonic across invalidation because JavaScript Realms can retain them. Existing hash contexts can finish across a transition. Release provider owners before unloading worker addon DSOs. Document provider-dependent availability and operation-specific restrictions. Add known-answer vectors, option validation, provider resolution, property-query, FIPS transition, worker, snapshot, and cross-API coverage. Refs: #62982 Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #65484 Backport-PR-URL: #65596 Fixes: #43040 Fixes: #64866 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 616bd3f commit d0d300f

19 files changed

Lines changed: 1952 additions & 179 deletions

File tree

deps/ncrypto/ncrypto.cc

Lines changed: 221 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#endif
1515
#include <algorithm>
1616
#include <array>
17+
#include <atomic>
1718
#include <climits>
1819
#include <cstring>
1920
#include <string_view>
@@ -507,23 +508,43 @@ DataPointer DataPointer::resize(size_t len) {
507508
}
508509

509510
// ============================================================================
510-
bool isFipsEnabled() {
511-
ClearErrorOnReturn clear_error_on_return;
511+
namespace {
512+
// This generation only coordinates cache invalidation. It does not make
513+
// OpenSSL default property changes safe to race with crypto operations.
514+
std::atomic<uint64_t> fips_state_generation{0};
515+
516+
bool isFipsEnabledRaw() {
512517
#if OPENSSL_VERSION_MAJOR >= 3
513518
return EVP_default_properties_is_fips_enabled(nullptr) == 1;
514519
#else
515520
return FIPS_mode() == 1;
516521
#endif
517522
}
523+
} // namespace
524+
525+
bool isFipsEnabled() {
526+
ClearErrorOnReturn clear_error_on_return;
527+
return isFipsEnabledRaw();
528+
}
518529

519530
bool setFipsEnabled(bool enable, CryptoErrorList* errors) {
520-
if (isFipsEnabled() == enable) return true;
531+
const bool was_enabled = isFipsEnabled();
532+
if (was_enabled == enable) return true;
521533
ClearErrorOnReturn clearErrorOnReturn(errors);
522534
#if OPENSSL_VERSION_MAJOR >= 3
523-
return EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1;
535+
const bool success =
536+
EVP_default_properties_enable_fips(nullptr, enable ? 1 : 0) == 1;
524537
#else
525-
return FIPS_mode_set(enable ? 1 : 0) == 1;
538+
const bool success = FIPS_mode_set(enable ? 1 : 0) == 1;
526539
#endif
540+
if (isFipsEnabledRaw() != was_enabled) {
541+
fips_state_generation.fetch_add(1, std::memory_order_release);
542+
}
543+
return success;
544+
}
545+
546+
uint64_t getFipsStateGeneration() {
547+
return fips_state_generation.load(std::memory_order_acquire);
527548
}
528549

529550
bool testFipsEnabled() {
@@ -4406,11 +4427,120 @@ bool SSLCtxPointer::setCipherSuites(const char* ciphers) {
44064427

44074428
// ============================================================================
44084429

4430+
namespace {
4431+
constexpr char AsciiToLower(char c) {
4432+
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
4433+
}
4434+
4435+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4436+
void PushAlgorithmAlias(const char* name, void* arg) {
4437+
if (name == nullptr) return;
4438+
static_cast<std::vector<std::string>*>(arg)->emplace_back(name);
4439+
}
4440+
#endif
4441+
} // namespace
4442+
44094443
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
44104444
Cipher::Cipher(DeleteFnPtr<EVP_CIPHER, EVP_CIPHER_free> cipher)
44114445
: cipher_(cipher.get()), fetched_cipher_(std::move(cipher)) {}
44124446
#endif
44134447

4448+
size_t CaseInsensitiveNameHash::operator()(
4449+
std::string_view name) const noexcept {
4450+
size_t hash = 5381;
4451+
for (char c : name) hash = ((hash << 5) + hash) ^ AsciiToLower(c);
4452+
return hash;
4453+
}
4454+
4455+
bool CaseInsensitiveNameEqual::operator()(std::string_view lhs,
4456+
std::string_view rhs) const noexcept {
4457+
if (lhs.size() != rhs.size()) return false;
4458+
for (size_t n = 0; n < lhs.size(); n++) {
4459+
if (AsciiToLower(lhs[n]) != AsciiToLower(rhs[n])) return false;
4460+
}
4461+
return true;
4462+
}
4463+
4464+
DigestCache::Result DigestCache::lookup(const char* name,
4465+
uint64_t generation) const {
4466+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4467+
if (generation_ != generation) return {};
4468+
const auto it = aliases_.find(name);
4469+
if (it == aliases_.end()) return {};
4470+
return lookup(it->second, generation);
4471+
#else
4472+
static_cast<void>(name);
4473+
static_cast<void>(generation);
4474+
return {};
4475+
#endif
4476+
}
4477+
4478+
DigestCache::Result DigestCache::insert(const char* name,
4479+
const EVP_MD* digest,
4480+
uint64_t generation) {
4481+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4482+
if (generation_ != generation || name == nullptr || digest == nullptr) {
4483+
return {};
4484+
}
4485+
4486+
const char* canonical_name = EVP_MD_get0_name(digest);
4487+
const OSSL_PROVIDER* provider = EVP_MD_get0_provider(digest);
4488+
if (canonical_name == nullptr || provider == nullptr) return {};
4489+
4490+
for (size_t index = 0; index < digests_.size(); index++) {
4491+
const EVP_MD* cached = digests_[index].get();
4492+
if (cached == nullptr) continue;
4493+
const char* cached_name = EVP_MD_get0_name(cached);
4494+
if (EVP_MD_get0_provider(cached) == provider && cached_name != nullptr &&
4495+
CaseInsensitiveNameEqual()(cached_name, canonical_name)) {
4496+
const int32_t id = static_cast<int32_t>(first_id_ + index);
4497+
aliases_.insert_or_assign(name, id);
4498+
return {cached, id};
4499+
}
4500+
}
4501+
4502+
if (next_id_ == UINT32_MAX ||
4503+
EVP_MD_up_ref(const_cast<EVP_MD*>(digest)) != 1) {
4504+
return {};
4505+
}
4506+
4507+
digests_.emplace_back(const_cast<EVP_MD*>(digest));
4508+
const int32_t id = static_cast<int32_t>(next_id_++);
4509+
const size_t index = digests_.size() - 1;
4510+
4511+
std::vector<std::string> aliases;
4512+
EVP_MD_names_do_all(digests_[index].get(), PushAlgorithmAlias, &aliases);
4513+
for (const std::string& alias : aliases) aliases_.emplace(alias, id);
4514+
aliases_.insert_or_assign(name, id);
4515+
4516+
return {digests_[index].get(), id};
4517+
#else
4518+
static_cast<void>(name);
4519+
static_cast<void>(digest);
4520+
static_cast<void>(generation);
4521+
return {};
4522+
#endif
4523+
}
4524+
4525+
void DigestCache::reset(uint64_t generation) {
4526+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4527+
if (generation_ == generation) return;
4528+
aliases_.clear();
4529+
digests_.clear();
4530+
first_id_ = next_id_;
4531+
#endif
4532+
generation_ = generation;
4533+
}
4534+
4535+
const DigestCache::AliasMap& DigestCache::aliases() const {
4536+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
4537+
return aliases_;
4538+
#else
4539+
static const AliasMap empty;
4540+
return empty;
4541+
#endif
4542+
}
4543+
44144544
Cipher::Cipher(const Cipher& other) : cipher_(other.cipher_) {
44154545
#if OPENSSL_WITH_AES_SIV || OPENSSL_WITH_AES_GCM_SIV
44164546
if (other.fetched_cipher_ != nullptr) {
@@ -6479,11 +6609,19 @@ EVP_MD_CTX* EVPMDCtxPointer::release() {
64796609
return ctx_.release();
64806610
}
64816611

6482-
bool EVPMDCtxPointer::digestInit(const Digest& digest) {
6612+
bool EVPMDCtxPointer::digestInit(const EVP_MD* digest) {
64836613
if (!ctx_) return false;
64846614
return EVP_DigestInit_ex(ctx_.get(), digest, nullptr) > 0;
64856615
}
64866616

6617+
#if !defined(OPENSSL_IS_BORINGSSL) && OPENSSL_VERSION_PREREQ(4, 0)
6618+
bool EVPMDCtxPointer::digestInit(const EVP_MD* digest,
6619+
const OSSL_PARAM* params) {
6620+
if (!ctx_) return false;
6621+
return EVP_DigestInit_ex2(ctx_.get(), digest, params) > 0;
6622+
}
6623+
#endif
6624+
64876625
bool EVPMDCtxPointer::digestUpdate(const Buffer<const void>& in) {
64886626
if (!ctx_) return false;
64896627
return EVP_DigestUpdate(ctx_.get(), in.data, in.len) > 0;
@@ -7009,7 +7147,10 @@ DataPointer xofHashDigest(const Buffer<const unsigned char>& buf,
70097147
if (ctx.digestInit(md) != 1) {
70107148
return {};
70117149
}
7012-
if (ctx.digestUpdate(reinterpret_cast<const Buffer<const void>&>(buf)) != 1) {
7150+
if (ctx.digestUpdate(Buffer<const void>{
7151+
.data = buf.data,
7152+
.len = buf.len,
7153+
}) != 1) {
70137154
return {};
70147155
}
70157156
return ctx.digestFinal(output_length);
@@ -7145,14 +7286,86 @@ size_t Digest::size() const {
71457286
return EVP_MD_size(md_);
71467287
}
71477288

7289+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
7290+
Digest::Digest(DeleteFnPtr<EVP_MD, EVP_MD_free> md)
7291+
: md_(md.get()), fetched_md_(std::move(md)) {}
7292+
#endif
7293+
7294+
Digest::Digest(const Digest& other) : md_(other.md_) {
7295+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
7296+
if (other.fetched_md_ != nullptr) {
7297+
if (EVP_MD_up_ref(other.fetched_md_.get()) == 1) {
7298+
fetched_md_.reset(other.fetched_md_.get());
7299+
} else {
7300+
md_ = nullptr;
7301+
}
7302+
}
7303+
#endif
7304+
}
7305+
7306+
Digest& Digest::operator=(const Digest& other) {
7307+
if (this == &other) return *this;
7308+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
7309+
if (other.fetched_md_ != nullptr) {
7310+
if (EVP_MD_up_ref(other.fetched_md_.get()) == 1) {
7311+
fetched_md_.reset(other.fetched_md_.get());
7312+
} else {
7313+
fetched_md_.reset();
7314+
md_ = nullptr;
7315+
return *this;
7316+
}
7317+
} else {
7318+
fetched_md_.reset();
7319+
}
7320+
#endif
7321+
md_ = other.md_;
7322+
return *this;
7323+
}
7324+
71487325
const Digest Digest::MD5 = Digest(EVP_md5());
71497326
const Digest Digest::SHA1 = Digest(EVP_sha1());
71507327
const Digest Digest::SHA256 = Digest(EVP_sha256());
71517328
const Digest Digest::SHA384 = Digest(EVP_sha384());
71527329
const Digest Digest::SHA512 = Digest(EVP_sha512());
71537330

7331+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
7332+
namespace {
7333+
bool IsSupportedDigest(const EVP_MD* md) {
7334+
if (md == nullptr || EVP_MD_is_a(md, "NULL")) return false;
7335+
7336+
// OpenSSL currently crashes when ML-DSA-MU finalizes an empty input. Keep it
7337+
// unavailable until the provider implementation is fixed.
7338+
// https://github.com/openssl/openssl/issues/32445
7339+
if (EVP_MD_is_a(md, "ML-DSA-MU")) return false;
7340+
7341+
return true;
7342+
}
7343+
} // namespace
7344+
#endif
7345+
71547346
const Digest Digest::FromName(const char* name) {
7155-
return ncrypto::getDigestByName(name);
7347+
const EVP_MD* md = ncrypto::getDigestByName(name);
7348+
if (md != nullptr) {
7349+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
7350+
if (md == EVP_md_null()) return Digest();
7351+
#endif
7352+
return Digest(md);
7353+
}
7354+
7355+
return Fetch(name);
7356+
}
7357+
7358+
const Digest Digest::Fetch(const char* name) {
7359+
#if NCRYPTO_USE_OPENSSL3_PROVIDER
7360+
MarkPopErrorOnReturn mark_pop_error_on_return;
7361+
DeleteFnPtr<EVP_MD, EVP_MD_free> fetched(
7362+
EVP_MD_fetch(nullptr, name, nullptr));
7363+
if (IsSupportedDigest(fetched.get())) {
7364+
return Digest(std::move(fetched));
7365+
}
7366+
#endif
7367+
7368+
return Digest();
71567369
}
71577370

71587371
// ============================================================================

0 commit comments

Comments
 (0)