Skip to content

Commit e75a363

Browse files
panvaaduh95
authored andcommitted
crypto: use EVP_MAC for HMAC on OpenSSL >=3
Closes: #59493 Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #63942Fixes: #59493 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent fcf32cf commit e75a363

12 files changed

Lines changed: 323 additions & 17 deletions

File tree

‎benchmark/crypto/create-hmac.js‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict';
2+
3+
constcommon=require('../common.js');
4+
const{ createHmac }=require('crypto');
5+
constassert=require('assert');
6+
7+
constbench=common.createBenchmark(main,{
8+
n: [1e5],
9+
algo: ['sha1','sha256','sha512'],
10+
keylen: [0,16,64,1024],
11+
});
12+
13+
functionmain({ n, algo, keylen }){
14+
constkey=Buffer.alloc(keylen,'k');
15+
consthmacs=newArray(n);
16+
17+
bench.start();
18+
for(leti=0;i<n;++i){
19+
hmacs[i]=createHmac(algo,key);
20+
}
21+
bench.end(n);
22+
23+
assert.strictEqual(typeofhmacs[n-1],'object');
24+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Throughput benchmark
2+
// creates a single HMAC, then pushes a bunch of data through it
3+
'use strict';
4+
5+
constcommon=require('../common.js');
6+
const{ createHmac }=require('crypto');
7+
8+
constbench=common.createBenchmark(main,{
9+
n: [500],
10+
algo: ['sha1','sha256','sha512'],
11+
keylen: [64],
12+
type: ['asc','utf','buf'],
13+
len: [2,1024,102400,1024*1024],
14+
api: ['update','stream'],
15+
});
16+
17+
functionmain({ api, type, len, algo, keylen, n }){
18+
letmessage;
19+
letencoding;
20+
switch(type){
21+
case'asc':
22+
message='a'.repeat(len);
23+
encoding='ascii';
24+
break;
25+
case'utf':
26+
message='\u00fc'.repeat(len/2);
27+
encoding='utf8';
28+
break;
29+
case'buf':
30+
message=Buffer.alloc(len,'b');
31+
break;
32+
default:
33+
thrownewError(`unknown message type: ${type}`);
34+
}
35+
36+
constfn=api==='stream' ? streamWrite : updateDigest;
37+
constkey=Buffer.alloc(keylen,'k');
38+
39+
bench.start();
40+
fn(algo,key,message,encoding,n,len);
41+
}
42+
43+
functionupdateDigest(algo,key,message,encoding,n,len){
44+
constwritten=n*len;
45+
constbits=written*8;
46+
constgbits=bits/(1024*1024*1024);
47+
consth=createHmac(algo,key);
48+
49+
while(n-->0)
50+
h.update(message,encoding);
51+
52+
h.digest();
53+
54+
bench.end(gbits);
55+
}
56+
57+
functionstreamWrite(algo,key,message,encoding,n,len){
58+
constwritten=n*len;
59+
constbits=written*8;
60+
constgbits=bits/(1024*1024*1024);
61+
consth=createHmac(algo,key);
62+
63+
while(n-->0)
64+
h.write(message,encoding);
65+
66+
h.end();
67+
h.read();
68+
69+
bench.end(gbits);
70+
}

‎benchmark/crypto/webcrypto-hmac.js‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
'use strict';
2+
3+
constcommon=require('../common.js');
4+
const{ subtle }=globalThis.crypto;
5+
6+
constsignParams={name: 'HMAC'};
7+
8+
letkeys;
9+
letcurrentHash;
10+
letcurrentKeyLength;
11+
12+
constbench=common.createBenchmark(main,{
13+
hash: ['SHA-1','SHA-256','SHA-512'],
14+
mode: ['serial','parallel'],
15+
keyReuse: ['shared','unique'],
16+
keylen: [512],
17+
len: [0,256,4096],
18+
n: [1e3],
19+
},{
20+
test: {
21+
keylen: 512,
22+
},
23+
combinationFilter(p){
24+
// Unique only differs from shared when operations overlap (parallel);
25+
// sequential calls have no contention so unique+serial adds no value.
26+
if(p.keyReuse==='unique')returnp.mode==='parallel';
27+
returntrue;
28+
},
29+
});
30+
31+
asyncfunctioncreateSigningKeys(n,hash,keylen){
32+
keys=newArray(n);
33+
currentHash=hash;
34+
currentKeyLength=keylen;
35+
36+
constalgorithm={name: 'HMAC', hash,length: keylen};
37+
constkey=awaitsubtle.generateKey(algorithm,true,['sign']);
38+
constraw=awaitsubtle.exportKey('raw',key);
39+
for(leti=0;i<n;++i){
40+
keys[i]=awaitsubtle.importKey('raw',raw,algorithm,false,['sign']);
41+
}
42+
}
43+
44+
asyncfunctionmeasureSerial(n,sharedKey,data){
45+
bench.start();
46+
for(leti=0;i<n;++i){
47+
awaitsubtle.sign(signParams,sharedKey||keys[i],data);
48+
}
49+
bench.end(n);
50+
}
51+
52+
asyncfunctionmeasureParallel(n,sharedKey,data){
53+
constpromises=newArray(n);
54+
bench.start();
55+
for(leti=0;i<n;++i){
56+
promises[i]=subtle.sign(signParams,sharedKey||keys[i],data);
57+
}
58+
awaitPromise.all(promises);
59+
bench.end(n);
60+
}
61+
62+
asyncfunctionmain({ n, mode, keyReuse, hash, keylen, len }){
63+
if(!keys||keys.length!==n||
64+
currentHash!==hash||currentKeyLength!==keylen){
65+
awaitcreateSigningKeys(n,hash,keylen);
66+
}
67+
68+
constdata=newUint8Array(len);
69+
data.fill(0x62);
70+
constsharedKey=keyReuse==='shared' ? keys[0] : undefined;
71+
72+
switch(mode){
73+
case'serial':
74+
awaitmeasureSerial(n,sharedKey,data);
75+
break;
76+
case'parallel':
77+
awaitmeasureParallel(n,sharedKey,data);
78+
break;
79+
}
80+
}

‎deps/ncrypto/ncrypto.cc‎

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4629,6 +4629,7 @@ bool extractP1363(const Buffer<const unsigned char>& buf,
46294629

46304630
// ============================================================================
46314631

4632+
#if !OPENSSL_WITH_EVP_MAC
46324633
HMACCtxPointer::HMACCtxPointer() : ctx_(nullptr) {}
46334634

46344635
HMACCtxPointer::HMACCtxPointer(HMAC_CTX* ctx) : ctx_(ctx) {}
@@ -4688,8 +4689,9 @@ bool HMACCtxPointer::digestInto(Buffer<void>* buf) {
46884689
HMACCtxPointer HMACCtxPointer::New() {
46894690
returnHMACCtxPointer(HMAC_CTX_new());
46904691
}
4692+
#endif// !OPENSSL_WITH_EVP_MAC
46914693

4692-
#ifOPENSSL_WITH_KMAC
4694+
#ifOPENSSL_WITH_EVP_MAC
46934695
EVPMacPointer::EVPMacPointer(EVP_MAC* mac) : mac_(mac) {}
46944696

46954697
EVPMacPointer::EVPMacPointer(EVPMacPointer&& other) noexcept
@@ -4777,7 +4779,92 @@ EVPMacCtxPointer EVPMacCtxPointer::New(EVP_MAC* mac) {
47774779
if (!mac) returnEVPMacCtxPointer();
47784780
returnEVPMacCtxPointer(EVP_MAC_CTX_new(mac));
47794781
}
4780-
#endif// OPENSSL_WITH_KMAC
4782+
4783+
HMACCtxPointer::HMACCtxPointer() = default;
4784+
4785+
HMACCtxPointer::HMACCtxPointer(EVPMacPointer&& mac, EVPMacCtxPointer&& ctx)
4786+
: mac_(std::move(mac)), ctx_(std::move(ctx)) {}
4787+
4788+
HMACCtxPointer::HMACCtxPointer(HMACCtxPointer&& other) noexcept
4789+
: mac_(std::move(other.mac_)),
4790+
ctx_(std::move(other.ctx_)),
4791+
md_size_(other.md_size_) {
4792+
other.md_size_ = 0;
4793+
}
4794+
4795+
HMACCtxPointer& HMACCtxPointer::operator=(HMACCtxPointer&& other) noexcept {
4796+
if (this == &other) return *this;
4797+
mac_ = std::move(other.mac_);
4798+
ctx_ = std::move(other.ctx_);
4799+
md_size_ = other.md_size_;
4800+
other.md_size_ = 0;
4801+
return *this;
4802+
}
4803+
4804+
HMACCtxPointer::~HMACCtxPointer() {
4805+
reset();
4806+
}
4807+
4808+
voidHMACCtxPointer::reset() {
4809+
ctx_.reset();
4810+
mac_.reset();
4811+
md_size_ = 0;
4812+
}
4813+
4814+
boolHMACCtxPointer::init(const Buffer<constvoid>& buf, const Digest& md) {
4815+
if (!ctx_ || !md) returnfalse;
4816+
4817+
constchar* md_name = EVP_MD_get0_name(md);
4818+
if (md_name == nullptr) returnfalse;
4819+
4820+
OSSL_PARAM params[] = {
4821+
OSSL_PARAM_construct_utf8_string(
4822+
OSSL_MAC_PARAM_DIGEST, const_cast<char*>(md_name), 0),
4823+
OSSL_PARAM_construct_end(),
4824+
};
4825+
4826+
if (!ctx_.init(buf, params)) returnfalse;
4827+
md_size_ = md.size();
4828+
returntrue;
4829+
}
4830+
4831+
boolHMACCtxPointer::update(const Buffer<constvoid>& buf) {
4832+
if (!ctx_) returnfalse;
4833+
return ctx_.update(buf);
4834+
}
4835+
4836+
DataPointer HMACCtxPointer::digest() {
4837+
if (md_size_ == 0) return {};
4838+
auto data = DataPointer::Alloc(md_size_);
4839+
if (!data) return {};
4840+
Buffer<void> buf = data;
4841+
if (!digestInto(&buf)) return {};
4842+
return data.resize(buf.len);
4843+
}
4844+
4845+
boolHMACCtxPointer::digestInto(Buffer<void>* buf) {
4846+
if (!ctx_) returnfalse;
4847+
4848+
size_t len = buf->len;
4849+
if (EVP_MAC_final(
4850+
ctx_.get(), static_cast<unsignedchar*>(buf->data), &len, buf->len) !=
4851+
1)
4852+
returnfalse;
4853+
4854+
buf->len = len;
4855+
returntrue;
4856+
}
4857+
4858+
HMACCtxPointer HMACCtxPointer::New() {
4859+
auto mac = EVPMacPointer::Fetch(OSSL_MAC_NAME_HMAC);
4860+
if (!mac) return {};
4861+
4862+
auto ctx = EVPMacCtxPointer::New(mac.get());
4863+
if (!ctx) return {};
4864+
4865+
returnHMACCtxPointer(std::move(mac), std::move(ctx));
4866+
}
4867+
#endif// OPENSSL_WITH_EVP_MAC
47814868

47824869
DataPointer hashDigest(const Buffer<constunsignedchar>& buf,
47834870
constEVP_MD* md) {

‎deps/ncrypto/ncrypto.h‎

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@
6565
#endif
6666

6767
#if OPENSSL_VERSION_PREREQ(3, 0)
68-
#defineOPENSSL_WITH_KMAC1
68+
#defineOPENSSL_WITH_EVP_MAC1
6969
#else
70-
#defineOPENSSL_WITH_KMAC0
70+
#defineOPENSSL_WITH_EVP_MAC0
7171
#endif
7272

7373
#if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_PREREQ(3, 2)
@@ -1528,6 +1528,7 @@ class EVPMDCtxPointer final {
15281528
DeleteFnPtr<EVP_MD_CTX, EVP_MD_CTX_free> ctx_;
15291529
};
15301530

1531+
#if !OPENSSL_WITH_EVP_MAC
15311532
classHMACCtxPointerfinal {
15321533
public:
15331534
HMACCtxPointer();
@@ -1554,8 +1555,9 @@ class HMACCtxPointer final {
15541555
private:
15551556
DeleteFnPtr<HMAC_CTX, HMAC_CTX_free> ctx_;
15561557
};
1558+
#endif// !OPENSSL_WITH_EVP_MAC
15571559

1558-
#ifOPENSSL_WITH_KMAC
1560+
#ifOPENSSL_WITH_EVP_MAC
15591561
classEVPMacPointerfinal {
15601562
public:
15611563
EVPMacPointer() = default;
@@ -1603,7 +1605,34 @@ class EVPMacCtxPointer final {
16031605
private:
16041606
DeleteFnPtr<EVP_MAC_CTX, EVP_MAC_CTX_free> ctx_;
16051607
};
1606-
#endif// OPENSSL_WITH_KMAC
1608+
1609+
classHMACCtxPointerfinal {
1610+
public:
1611+
HMACCtxPointer();
1612+
HMACCtxPointer(HMACCtxPointer&& other) noexcept;
1613+
HMACCtxPointer& operator=(HMACCtxPointer&& other) noexcept;
1614+
NCRYPTO_DISALLOW_COPY(HMACCtxPointer)
1615+
~HMACCtxPointer();
1616+
1617+
inlinebooloperator==(std::nullptr_t) noexcept { return ctx_ == nullptr; }
1618+
inlineoperatorbool() const { return ctx_ != nullptr; }
1619+
voidreset();
1620+
1621+
boolinit(const Buffer<constvoid>& buf, const Digest& md);
1622+
boolupdate(const Buffer<constvoid>& buf);
1623+
DataPointer digest();
1624+
booldigestInto(Buffer<void>* buf);
1625+
1626+
static HMACCtxPointer New();
1627+
1628+
private:
1629+
HMACCtxPointer(EVPMacPointer&& mac, EVPMacCtxPointer&& ctx);
1630+
1631+
EVPMacPointer mac_;
1632+
EVPMacCtxPointer ctx_;
1633+
size_t md_size_ = 0;
1634+
};
1635+
#endif// OPENSSL_WITH_EVP_MAC
16071636

16081637
#ifndef OPENSSL_NO_ENGINE
16091638
classEnginePointerfinal {

‎src/crypto/README.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,18 @@ using ECPointPointer = DeleteFnPtr<EC_POINT, EC_POINT_free>;
9090
using ECKeyPointer = DeleteFnPtr<EC_KEY, EC_KEY_free>;
9191
using DHPointer = DeleteFnPtr<DH, DH_free>;
9292
using ECDSASigPointer = DeleteFnPtr<ECDSA_SIG, ECDSA_SIG_free>;
93-
using HMACCtxPointer = DeleteFnPtr<HMAC_CTX, HMAC_CTX_free>;
9493
using CipherCtxPointer = DeleteFnPtr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free>;
9594
```
9695

9796
Examples of these being used are pervasive through the `src/crypto` code.
9897

98+
`HMACCtxPointer` is a dedicated HMAC state wrapper rather than a plain
99+
`DeleteFnPtr` alias. On OpenSSL 3 and later it owns the provider-backed
100+
`EVP_MAC`/`EVP_MAC_CTX` state. On OpenSSL 1.1.1 and BoringSSL it owns the
101+
legacy `HMAC_CTX` state. HMAC call sites should use `HMACCtxPointer::New()`,
102+
`init()`, `update()`, and `digest()`/`digestInto()` so the backend selection
103+
stays contained in ncrypto.
104+
99105
### `ByteSource`
100106

101107
The `ByteSource` class is a helper utility representing a _read-only_ byte

‎src/crypto/crypto_hmac.cc‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ using v8::Uint32;
3030
using v8::Value;
3131

3232
namespacecrypto {
33-
Hmac::Hmac(Environment* env, Local<Object> wrap)
34-
: BaseObject(env, wrap),
35-
ctx_(nullptr) {
33+
Hmac::Hmac(Environment* env, Local<Object> wrap) : BaseObject(env, wrap) {
3634
MakeWeak();
3735
}
3836

0 commit comments

Comments
 (0)