Skip to content

Commit 33d28a1

Browse files
anonrigaduh95
authored andcommitted
crypto: reuse HMAC contexts in createHmac()
Every HMAC context was created with EVP_MAC_fetch() and keyed with the digest passed by name, so the provider fetched the digest again on each init. Both lookups go through OpenSSL's method store. Keep an HMAC context without a key for each digest in the per-Environment digest cache, and duplicate it in createHmac(). A duplicated context already has its digest, so keying it with the same digest no longer fetches anything. Signed-off-by: Yagiz Nizipli <yagiz@nizipli.com> Assisted-by: Grok PR-URL: #67109 Reviewed-By: Filip Skokan <panva.ip@gmail.com>
1 parent b9248dc commit 33d28a1

5 files changed

Lines changed: 92 additions & 1 deletion

File tree

‎deps/ncrypto/ncrypto.cc‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5506,16 +5506,39 @@ DigestCache::Result DigestCache::insert(const char* name,
55065506
#endif
55075507
}
55085508

5509+
DigestCache::~DigestCache() = default;
5510+
55095511
void DigestCache::reset(uint64_t generation) {
55105512
#if NCRYPTO_USE_OPENSSL_PROVIDER
55115513
if (generation_ == generation) return;
55125514
aliases_.clear();
5515+
hmac_templates_.clear();
55135516
digests_.clear();
55145517
first_id_ = next_id_;
55155518
#endif
55165519
generation_ = generation;
55175520
}
55185521

5522+
#if NCRYPTO_USE_OPENSSL_PROVIDER
5523+
const HMACCtxPointer* DigestCache::getHmacTemplate(const EVP_MD* digest,
5524+
uint64_t generation) {
5525+
if (generation_ != generation || digest == nullptr) return nullptr;
5526+
for (size_t index = 0; index < digests_.size(); index++) {
5527+
if (digests_[index].get() != digest) continue;
5528+
if (hmac_templates_.size() <= index) {
5529+
hmac_templates_.resize(digests_.size());
5530+
}
5531+
if (!hmac_templates_[index]) {
5532+
auto ctx = HMACCtxPointer::New(Digest::Ref(digest));
5533+
if (!ctx) return nullptr;
5534+
hmac_templates_[index] = std::make_unique<HMACCtxPointer>(std::move(ctx));
5535+
}
5536+
return hmac_templates_[index].get();
5537+
}
5538+
return nullptr;
5539+
}
5540+
#endif
5541+
55195542
const DigestCache::AliasMap& DigestCache::aliases() const {
55205543
#if NCRYPTO_USE_OPENSSL_PROVIDER
55215544
return aliases_;
@@ -8584,15 +8607,19 @@ HMACCtxPointer::HMACCtxPointer(EVPMacPointer&& mac, EVPMacCtxPointer&& ctx)
85848607
HMACCtxPointer::HMACCtxPointer(HMACCtxPointer&& other) noexcept
85858608
: mac_(std::move(other.mac_)),
85868609
ctx_(std::move(other.ctx_)),
8610+
md_(other.md_),
85878611
md_size_(other.md_size_) {
8612+
other.md_ = nullptr;
85888613
other.md_size_ = 0;
85898614
}
85908615

85918616
HMACCtxPointer& HMACCtxPointer::operator=(HMACCtxPointer&& other) noexcept {
85928617
if (this == &other) return *this;
85938618
mac_ = std::move(other.mac_);
85948619
ctx_ = std::move(other.ctx_);
8620+
md_ = other.md_;
85958621
md_size_ = other.md_size_;
8622+
other.md_ = nullptr;
85968623
other.md_size_ = 0;
85978624
return *this;
85988625
}
@@ -8604,11 +8631,13 @@ HMACCtxPointer::~HMACCtxPointer() {
86048631
void HMACCtxPointer::reset() {
86058632
ctx_.reset();
86068633
mac_.reset();
8634+
md_ = nullptr;
86078635
md_size_ = 0;
86088636
}
86098637

86108638
bool HMACCtxPointer::init(const Buffer<const void>& buf, const Digest& md) {
86118639
if (!ctx_ || !md) return false;
8640+
if (md.get() == md_) return ctx_.init(buf);
86128641

86138642
const char* md_name = EVP_MD_get0_name(md);
86148643
if (md_name == nullptr) return false;
@@ -8620,6 +8649,7 @@ bool HMACCtxPointer::init(const Buffer<const void>& buf, const Digest& md) {
86208649
};
86218650

86228651
if (!ctx_.init(buf, params)) return false;
8652+
md_ = md.get();
86238653
md_size_ = md.size();
86248654
return true;
86258655
}
@@ -8651,6 +8681,16 @@ bool HMACCtxPointer::digestInto(Buffer<void>* buf) {
86518681
return true;
86528682
}
86538683

8684+
HMACCtxPointer HMACCtxPointer::clone() const {
8685+
if (!ctx_) return {};
8686+
EVPMacCtxPointer ctx(EVP_MAC_CTX_dup(ctx_.get()));
8687+
if (!ctx) return {};
8688+
HMACCtxPointer result(EVPMacPointer(), std::move(ctx));
8689+
result.md_ = md_;
8690+
result.md_size_ = md_size_;
8691+
return result;
8692+
}
8693+
86548694
HMACCtxPointer HMACCtxPointer::New() {
86558695
auto mac = EVPMacPointer::Fetch(OSSL_MAC_NAME_HMAC);
86568696
if (!mac) return {};
@@ -8660,6 +8700,25 @@ HMACCtxPointer HMACCtxPointer::New() {
86608700

86618701
return HMACCtxPointer(std::move(mac), std::move(ctx));
86628702
}
8703+
8704+
HMACCtxPointer HMACCtxPointer::New(const Digest& md) {
8705+
if (!md) return {};
8706+
const char* md_name = EVP_MD_get0_name(md);
8707+
if (md_name == nullptr) return {};
8708+
8709+
auto result = New();
8710+
if (!result) return {};
8711+
8712+
OSSL_PARAM params[] = {
8713+
OSSL_PARAM_construct_utf8_string(
8714+
OSSL_MAC_PARAM_DIGEST, const_cast<char*>(md_name), 0),
8715+
OSSL_PARAM_construct_end(),
8716+
};
8717+
if (EVP_MAC_CTX_set_params(result.ctx_.get(), params) != 1) return {};
8718+
result.md_ = md.get();
8719+
result.md_size_ = md.size();
8720+
return result;
8721+
}
86638722
#endif // OPENSSL_WITH_EVP_MAC
86648723

86658724
DataPointer hashDigest(const Buffer<const unsigned char>& buf,

‎deps/ncrypto/ncrypto.h‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ class DataPointer;
307307
class DHPointer;
308308
class ECKeyPointer;
309309
class EVPKeyPointer;
310+
class HMACCtxPointer;
310311
class KeyAlgorithm;
311312
class MacCache;
312313
class EVPMacCtxPointer;
@@ -403,6 +404,7 @@ class DigestCache final {
403404
CaseInsensitiveNameEqual>;
404405

405406
DigestCache() = default;
407+
~DigestCache();
406408
NCRYPTO_DISALLOW_COPY_AND_MOVE(DigestCache)
407409

408410
Result lookup(const char* name, uint64_t generation) const;
@@ -425,6 +427,12 @@ class DigestCache final {
425427
Result insert(const char* name, const EVP_MD* digest, uint64_t generation);
426428
void reset(uint64_t generation);
427429
const AliasMap& aliases() const;
430+
#if NCRYPTO_USE_OPENSSL_PROVIDER
431+
// Returns an HMAC context for the cached digest that has no key yet.
432+
// Duplicating it avoids fetching HMAC and the digest for every context.
433+
const HMACCtxPointer* getHmacTemplate(const EVP_MD* digest,
434+
uint64_t generation);
435+
#endif
428436

429437
private:
430438
uint64_t generation_ = 0;
@@ -436,6 +444,7 @@ class DigestCache final {
436444
uint32_t first_id_ = 0;
437445
uint32_t next_id_ = 0;
438446
std::vector<EVPMDPointer> digests_;
447+
std::vector<std::unique_ptr<HMACCtxPointer>> hmac_templates_;
439448
AliasMap aliases_;
440449
#endif
441450
};
@@ -2151,14 +2160,19 @@ class HMACCtxPointer final {
21512160
bool update(const Buffer<const void>& buf);
21522161
DataPointer digest();
21532162
bool digestInto(Buffer<void>* buf);
2163+
HMACCtxPointer clone() const;
21542164

21552165
static HMACCtxPointer New();
2166+
// Creates a context that already uses md, so that init() does not have to
2167+
// fetch md again when it is called with the same digest.
2168+
static HMACCtxPointer New(const Digest& md);
21562169

21572170
private:
21582171
HMACCtxPointer(EVPMacPointer&& mac, EVPMacCtxPointer&& ctx);
21592172

21602173
EVPMacPointer mac_;
21612174
EVPMacCtxPointer ctx_;
2175+
const EVP_MD* md_ = nullptr;
21622176
size_t md_size_ = 0;
21632177
};
21642178
#endif // OPENSSL_WITH_EVP_MAC

‎src/crypto/crypto_hash.cc‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,18 @@ const ncrypto::Digest GetDigestForJob(Environment* env,
382382
return ncrypto::Digest::FromName(name);
383383
}
384384

385+
ncrypto::HMACCtxPointer NewHmacContext(Environment* env,
386+
const ncrypto::Digest& md) {
387+
#if NCRYPTO_USE_OPENSSL_PROVIDER
388+
SynchronizeHashCache(env);
389+
if (const auto* hmac_template = env->provider_digest_cache->getHmacTemplate(
390+
md, env->hash_cache_generation)) {
391+
if (auto ctx = hmac_template->clone()) return ctx;
392+
}
393+
#endif
394+
return ncrypto::HMACCtxPointer::New();
395+
}
396+
385397
void MarkInvalidXofLength() {
386398
#if NCRYPTO_USE_OPENSSL_PROVIDER
387399
ERR_raise(ERR_LIB_EVP, EVP_R_NOT_XOF_OR_INVALID_LENGTH);

‎src/crypto/crypto_hash.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ const ncrypto::Digest GetDigestForJob(Environment* env,
2424
CryptoJobMode mode,
2525
const char* name);
2626

27+
// Returns an HMAC context to be keyed with md. Contexts for digests from
28+
// GetDigest() are duplicated from a cached template, so keying them does not
29+
// fetch HMAC or md again.
30+
ncrypto::HMACCtxPointer NewHmacContext(Environment* env,
31+
const ncrypto::Digest& md);
32+
2733
class Hash final : public BaseObject {
2834
public:
2935
static void Initialize(Environment* env, v8::Local<v8::Object> target);

‎src/crypto/crypto_hmac.cc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
7979
key = "";
8080
}
8181

82-
ctx_ = HMACCtxPointer::New();
82+
ctx_ = NewHmacContext(env(), md);
8383
ncrypto::Buffer<const void> key_buf{
8484
.data = key,
8585
.len = static_cast<size_t>(key_len),

0 commit comments

Comments
 (0)