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

crypto: implement crypto.hash() #51044

Closed
wants to merge 3 commits into from

Conversation

joyeecheung
Copy link
Member

@joyeecheung joyeecheung commented Dec 4, 2023

crypto: implement crypto.hash()

This patch introduces a helper crypto.hash() that computes
a digest from the input at one shot. This can be 1.2-2x faster
than the object-based createHash() for smaller inputs (<= 5MB)
that are readily available (not streamed) and incur less memory
overhead since no intermediate objects will be created.

Refs: nodejs/performance#136

From the benchmark CI https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1493/console

22:46:22                                                                           confidence improvement accuracy (*)    (**)   (***)
22:46:22 crypto/oneshot-hash.js n=1000 type='buffer' method='md5' length=1000             ***     76.69 %      ±11.25% ±15.02% ±19.65%
22:46:22 crypto/oneshot-hash.js n=1000 type='buffer' method='md5' length=100000            **     12.44 %       ±8.53% ±11.36% ±14.78%
22:46:22 crypto/oneshot-hash.js n=1000 type='buffer' method='sha1' length=1000            ***     72.44 %      ±11.25% ±15.01% ±19.61%
22:46:22 crypto/oneshot-hash.js n=1000 type='buffer' method='sha1' length=100000                  -1.19 %       ±6.72%  ±8.94% ±11.65%
22:46:22 crypto/oneshot-hash.js n=1000 type='buffer' method='sha256' length=1000          ***     65.75 %      ±11.03% ±14.71% ±19.22%
22:46:22 crypto/oneshot-hash.js n=1000 type='buffer' method='sha256' length=100000                -1.41 %       ±2.99%  ±3.99%  ±5.22%
22:46:22 crypto/oneshot-hash.js n=1000 type='string' method='md5' length=1000             ***     98.77 %      ±15.43% ±20.67% ±27.18%
22:46:22 crypto/oneshot-hash.js n=1000 type='string' method='md5' length=100000                    1.43 %       ±3.17%  ±4.21%  ±5.48%
22:46:22 crypto/oneshot-hash.js n=1000 type='string' method='sha1' length=1000            ***    111.56 %      ±17.04% ±22.85% ±30.09%
22:46:22 crypto/oneshot-hash.js n=1000 type='string' method='sha1' length=100000                   1.67 %       ±3.73%  ±4.96%  ±6.46%
22:46:22 crypto/oneshot-hash.js n=1000 type='string' method='sha256' length=1000          ***     88.15 %      ±13.62% ±18.19% ±23.83%
22:46:22 crypto/oneshot-hash.js n=1000 type='string' method='sha256' length=100000                 2.00 %       ±2.05%  ±2.73%  ±3.56%
22:46:22 crypto/oneshot-hash.js n=100000 type='buffer' method='md5' length=1000           ***     39.52 %       ±6.05%  ±8.11% ±10.67%
22:46:22 crypto/oneshot-hash.js n=100000 type='buffer' method='sha1' length=1000          ***     41.96 %       ±7.36%  ±9.86% ±12.95%
22:46:22 crypto/oneshot-hash.js n=100000 type='buffer' method='sha256' length=1000        ***     23.13 %       ±3.97%  ±5.31%  ±6.96%
22:46:22 crypto/oneshot-hash.js n=100000 type='string' method='md5' length=1000           ***     23.50 %       ±3.04%  ±4.06%  ±5.30%
22:46:22 crypto/oneshot-hash.js n=100000 type='string' method='sha1' length=1000          ***     30.30 %       ±3.67%  ±4.89%  ±6.39%
22:46:22 crypto/oneshot-hash.js n=100000 type='string' method='sha256' length=1000        ***     18.63 %       ±3.12%  ±4.16%  ±5.43%
Benchmark results from macOS + M2
                                                                         confidence improvement accuracy (*)    (**)   (***)
crypto/node-digest.js n=1000 type='buffer' method='md5' length=1000             ***     24.64 %       ±6.24%  ±8.33% ±10.92%
crypto/node-digest.js n=1000 type='buffer' method='md5' length=100000                    1.82 %       ±1.87%  ±2.50%  ±3.26%
crypto/node-digest.js n=1000 type='buffer' method='sha1' length=1000            ***     46.77 %       ±4.13%  ±5.49%  ±7.15%
crypto/node-digest.js n=1000 type='buffer' method='sha1' length=100000                   1.99 %       ±3.24%  ±4.32%  ±5.65%
crypto/node-digest.js n=1000 type='buffer' method='sha256' length=1000          ***     46.38 %       ±3.38%  ±4.52%  ±5.93%
crypto/node-digest.js n=1000 type='buffer' method='sha256' length=100000                 1.81 %       ±2.71%  ±3.61%  ±4.70%
crypto/node-digest.js n=1000 type='string' method='md5' length=1000             ***     25.20 %       ±7.60% ±10.13% ±13.22%
crypto/node-digest.js n=1000 type='string' method='md5' length=100000             *      3.22 %       ±2.45%  ±3.28%  ±4.33%
crypto/node-digest.js n=1000 type='string' method='sha1' length=1000            ***     41.89 %       ±4.25%  ±5.67%  ±7.41%
crypto/node-digest.js n=1000 type='string' method='sha1' length=100000                   0.99 %       ±4.32%  ±5.77%  ±7.57%
crypto/node-digest.js n=1000 type='string' method='sha256' length=1000          ***     42.50 %       ±4.12%  ±5.50%  ±7.20%
crypto/node-digest.js n=1000 type='string' method='sha256' length=100000                -0.56 %       ±3.69%  ±4.96%  ±6.56%
crypto/node-digest.js n=100000 type='buffer' method='md5' length=1000           ***     17.91 %       ±4.82%  ±6.42%  ±8.35%
crypto/node-digest.js n=100000 type='buffer' method='sha1' length=1000          ***     47.75 %       ±2.83%  ±3.77%  ±4.92%
crypto/node-digest.js n=100000 type='buffer' method='sha256' length=1000        ***     56.42 %       ±2.75%  ±3.66%  ±4.79%
crypto/node-digest.js n=100000 type='string' method='md5' length=1000           ***     15.45 %       ±2.45%  ±3.28%  ±4.32%
crypto/node-digest.js n=100000 type='string' method='sha1' length=1000          ***     32.16 %       ±4.69%  ±6.27%  ±8.22%
crypto/node-digest.js n=100000 type='string' method='sha256' length=1000        ***     34.65 %       ±2.42%  ±3.23%  ±4.23%
Benchmark results from Ubuntu + Intel(R) Xeon(R) Platinum 8280 CPU @ 2.70GHz
                                                                         confidence improvement accuracy (*)   (**)  (***)
crypto/node-digest.js n=1000 type='buffer' method='md5' length=1000             ***     50.52 %       ±3.07% ±4.09% ±5.34%
crypto/node-digest.js n=1000 type='buffer' method='md5' length=100000            **      1.31 %       ±0.79% ±1.05% ±1.37%
crypto/node-digest.js n=1000 type='buffer' method='sha1' length=1000            ***     58.90 %       ±4.88% ±6.54% ±8.62%
crypto/node-digest.js n=1000 type='buffer' method='sha1' length=100000          ***      3.13 %       ±1.21% ±1.62% ±2.11%
crypto/node-digest.js n=1000 type='buffer' method='sha256' length=1000          ***     43.08 %       ±3.01% ±4.02% ±5.25%
crypto/node-digest.js n=1000 type='buffer' method='sha256' length=100000        ***      1.31 %       ±0.70% ±0.93% ±1.21%
crypto/node-digest.js n=1000 type='string' method='md5' length=1000             ***     45.39 %       ±3.28% ±4.40% ±5.78%
crypto/node-digest.js n=1000 type='string' method='md5' length=100000            **      1.17 %       ±0.77% ±1.02% ±1.33%
crypto/node-digest.js n=1000 type='string' method='sha1' length=1000            ***     52.71 %       ±2.93% ±3.93% ±5.18%
crypto/node-digest.js n=1000 type='string' method='sha1' length=100000           **      1.81 %       ±1.06% ±1.42% ±1.85%
crypto/node-digest.js n=1000 type='string' method='sha256' length=1000          ***     41.00 %       ±2.62% ±3.50% ±4.58%
crypto/node-digest.js n=1000 type='string' method='sha256' length=100000         **      0.90 %       ±0.54% ±0.72% ±0.93%
crypto/node-digest.js n=100000 type='buffer' method='md5' length=1000           ***     55.57 %       ±1.94% ±2.60% ±3.42%
crypto/node-digest.js n=100000 type='buffer' method='sha1' length=1000          ***     66.81 %       ±1.57% ±2.09% ±2.72%
crypto/node-digest.js n=100000 type='buffer' method='sha256' length=1000        ***     39.65 %       ±1.16% ±1.55% ±2.03%
crypto/node-digest.js n=100000 type='string' method='md5' length=1000           ***     38.61 %       ±1.55% ±2.07% ±2.71%
crypto/node-digest.js n=100000 type='string' method='sha1' length=1000          ***     43.45 %       ±1.20% ±1.59% ±2.07%
crypto/node-digest.js n=100000 type='string' method='sha256' length=1000        ***     31.46 %       ±1.33% ±1.77% ±2.30%

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/crypto

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Dec 4, 2023
@panva
Copy link
Member

panva commented Dec 4, 2023

Interested, could the same be done for hmac?

@joyeecheung
Copy link
Member Author

Results of comparing the one-shot digest implementation from this, the createHash() from #51034 and the main branch using a microbenchmark from nodejs/performance#136 (comment) on macOS + M2:

$ out/Release/node hash.js
612.720917
1000000 hashes, input length 1000, 1000000 results
$ ./node_51034 hash.js
930.777417
1000000 hashes, input length 1000, 1000000 results
$ ./node_main hash.js
1111.237292
1000000 hashes, input length 1000, 1000000 results

On Ubuntu + Intel(R) Xeon(R) Platinum 8280 CPU @ 2.70GHz

$ out/Release/node hash.js
1451.8053810000001
1000000 hashes, input length 1000, 1000000 results
$ ./node_51034 hash.js
2596.804682
1000000 hashes, input length 1000, 1000000 results
$ ./node_main hash.js
3014.865648
1000000 hashes, input length 1000, 1000000 results

@joyeecheung
Copy link
Member Author

Interested, could the same be done for hmac?

Yes though I think for caching EVP_MAC for maximum speed we need to do the init-update-final ourselves because for HAMC there's only EVP_Q_mac which always fetches the implementation on its own.

@tniessen
Copy link
Member

tniessen commented Dec 5, 2023

I suppose this supersedes #42233?

@joyeecheung
Copy link
Member Author

joyeecheung commented Dec 5, 2023

I suppose this supersedes #42233?

If the goal is to "reducing as much unnecessary overhead as possible when computing a one-shot digest synchronously" then I think yes. With an implementation based on HashJob there would still be quite a lot of overhead, whereas not creating any BaseObject + explicit fetching via EVP_MD_fetch + caching EVP_MD* + mapping encoding/algorithm from string to ids + using EVP_Digest directly would shave off a lot more or get rid of most of the bottleneck that's still under Node.js's control.

I don't know if the current API is the best, however. But I think that might already be enough for maybe 80% of the use case (this assumes the string input is decoded as UTF8 - would the need of custom input decoding really be that common? With this API users can still decode their strings with other encodings into a buffer and use that as input if they really need a different encoding, though)

@joyeecheung joyeecheung force-pushed the oneshot-digest branch 6 times, most recently from 444fec1 to 114f2f9 Compare January 6, 2024 21:18
@joyeecheung joyeecheung changed the title crypto: implement crypto.digest() crypto: implement crypto.hash() Jan 14, 2024
@joyeecheung joyeecheung added the request-ci Add this label to start a Jenkins CI on a PR. label Jan 14, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jan 14, 2024
@nodejs-github-bot
Copy link
Collaborator

@joyeecheung joyeecheung added the request-ci Add this label to start a Jenkins CI on a PR. label Jan 19, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jan 19, 2024
@nodejs-github-bot
Copy link
Collaborator

@joyeecheung joyeecheung marked this pull request as ready for review January 23, 2024 12:40
@joyeecheung
Copy link
Member Author

Added docs and renamed this to crypto.hash (because if we were to follow up with the same thing for HMAC later, crypto.digest() would be ambiguous). Can I get some review please? @nodejs/cpp-reviewers @nodejs/crypto

@joyeecheung
Copy link
Member Author

Pinging @nodejs/cpp-reviewers @nodejs/crypto again..

Copy link
Contributor

@aduh95 aduh95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't really vouch for the C++ code, but JS code and benchmark LGTM 👍 Could you add a test that validates the correct error is returned when passing arguments with wrong types?

doc/api/crypto.md Outdated Show resolved Hide resolved
doc/api/crypto.md Outdated Show resolved Hide resolved
Copy link
Member

@H4ad H4ad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work, I'm sure many libraries will see a good impact on performance with this new method.

@nodejs/performance Ping to get more attention/approvals.

lib/internal/crypto/hash.js Outdated Show resolved Hide resolved
This patch introduces a helper crypto.hash() that computes
a digest from the input at one shot. This can be 1.2-1.6x faster
than the object-based createHash() for smaller inputs (<= 5MB)
that are readily available (not streamed) and incur less memory
overhead since no intermediate objects will be created.
marco-ippolito added a commit that referenced this pull request Mar 2, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) #51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) #51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) #51794
doc:
  * add zcbenz to collaborators (Cheng Zhao) #51812
  * add lemire to collaborators (Daniel Lemire) #51572
http2:
  * (SEMVER-MINOR) add h2 compat support for appendHeader (Tim Perry) #51412
  * (SEMVER-MINOR) add server handshake utility (snek) #51172
  * (SEMVER-MINOR) receive customsettings (Marten Richter) #51323
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) #51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) #50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) #50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) #50960
  * (SEMVER-MINOR) support multi-line values for .env file (IlyasShabi) #51289
  * (SEMVER-MINOR) add `process.loadEnvFile` and `util.parseEnv` (Yagiz Nizipli) #51476
  * (SEMVER-MINOR) do not coerce dotenv paths (Tobias Nießen) #51425
stream:
  * (SEMVER-MINOR) implement `min` option for `ReadableStreamBYOBReader.read` (Mattias Buelens) #50888
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) #51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) #51244

PR-URL: #51932
@panva
Copy link
Member

panva commented Mar 4, 2024

The default encoding being hex is kind of awkward and not inline with our other APIs. Should we pause inclusion of this in the upcoming release and reconsider/discuss to have the default be a buffer?

@tniessen
Copy link
Member

tniessen commented Mar 4, 2024

We could at least mark this as experimental if it isn't already.

@joyeecheung
Copy link
Member Author

joyeecheung commented Mar 4, 2024

From what I can tell, when people do one-shot hashing, most of the time they want the result to be encoded in a hex string - or from a quick search in popular packages, it seems rare for them to want the result to be returned in a buffer. What exactly makes this default awkward other than that it follows what most users want instead of what other APIs already do?

Personally it doesn't seem very necessary to me to return the hash in buffer by default in other APIs either, though they are already what they are. The hash is small and fixed-sized, and creating a buffer is not cheap. Encoding it to string isn't, either - for less than a megabyte of input, the buffer + string allocation are actually more expensive than the actual hashing (and the most common use case of this that I can see in top packages is hashing JS files, which are normally around this size).

@joyeecheung
Copy link
Member Author

joyeecheung commented Mar 4, 2024

I agree we can mark it as 1.2, though, if users do think defaulting to buffer makes sense, we can switch to buffer before we make it 2.

marco-ippolito added a commit that referenced this pull request Mar 5, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) #51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) #51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) #51794
doc:
  * add zcbenz to collaborators (Cheng Zhao) #51812
  * add lemire to collaborators (Daniel Lemire) #51572
http2:
  * (SEMVER-MINOR) add h2 compat support for appendHeader (Tim Perry) #51412
  * (SEMVER-MINOR) add server handshake utility (snek) #51172
  * (SEMVER-MINOR) receive customsettings (Marten Richter) #51323
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) #51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) #50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) #50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) #50960
  * (SEMVER-MINOR) support multi-line values for .env file (IlyasShabi) #51289
  * (SEMVER-MINOR) add `process.loadEnvFile` and `util.parseEnv` (Yagiz Nizipli) #51476
  * (SEMVER-MINOR) do not coerce dotenv paths (Tobias Nießen) #51425
stream:
  * (SEMVER-MINOR) implement `min` option for `ReadableStreamBYOBReader.read` (Mattias Buelens) #50888
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) #51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) #51244

PR-URL: #51932
marco-ippolito added a commit that referenced this pull request Mar 5, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) #51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) #51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) #51794
doc:
  * add zcbenz to collaborators (Cheng Zhao) #51812
  * add lemire to collaborators (Daniel Lemire) #51572
http2:
  * (SEMVER-MINOR) add h2 compat support for appendHeader (Tim Perry) #51412
  * (SEMVER-MINOR) add server handshake utility (snek) #51172
  * (SEMVER-MINOR) receive customsettings (Marten Richter) #51323
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) #51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) #50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) #50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) #50960
  * (SEMVER-MINOR) support multi-line values for .env file (IlyasShabi) #51289
  * (SEMVER-MINOR) add `process.loadEnvFile` and `util.parseEnv` (Yagiz Nizipli) #51476
  * (SEMVER-MINOR) do not coerce dotenv paths (Tobias Nießen) #51425
stream:
  * (SEMVER-MINOR) implement `min` option for `ReadableStreamBYOBReader.read` (Mattias Buelens) #50888
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) #51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) #51244

PR-URL: #51932
RafaelGSS pushed a commit that referenced this pull request Mar 6, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) #51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) #51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) #51794
doc:
  * add zcbenz to collaborators (Cheng Zhao) #51812
  * add lemire to collaborators (Daniel Lemire) #51572
http2:
  * (SEMVER-MINOR) add h2 compat support for appendHeader (Tim Perry) #51412
  * (SEMVER-MINOR) add server handshake utility (snek) #51172
  * (SEMVER-MINOR) receive customsettings (Marten Richter) #51323
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) #51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) #50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) #50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) #50960
  * (SEMVER-MINOR) support multi-line values for .env file (IlyasShabi) #51289
  * (SEMVER-MINOR) add `process.loadEnvFile` and `util.parseEnv` (Yagiz Nizipli) #51476
  * (SEMVER-MINOR) do not coerce dotenv paths (Tobias Nießen) #51425
stream:
  * (SEMVER-MINOR) implement `min` option for `ReadableStreamBYOBReader.read` (Mattias Buelens) #50888
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) #51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) #51244

PR-URL: #51932
richardlau pushed a commit that referenced this pull request Mar 25, 2024
PR-URL: #51044
Refs: nodejs/performance#136
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
richardlau pushed a commit that referenced this pull request Mar 25, 2024
This patch introduces a helper crypto.hash() that computes
a digest from the input at one shot. This can be 1.2-1.6x faster
than the object-based createHash() for smaller inputs (<= 5MB)
that are readily available (not streamed) and incur less memory
overhead since no intermediate objects will be created.

PR-URL: #51044
Refs: nodejs/performance#136
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
richardlau pushed a commit that referenced this pull request Mar 25, 2024
PR-URL: #51044
Refs: nodejs/performance#136
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
richardlau pushed a commit that referenced this pull request Mar 25, 2024
This patch introduces a helper crypto.hash() that computes
a digest from the input at one shot. This can be 1.2-1.6x faster
than the object-based createHash() for smaller inputs (<= 5MB)
that are readily available (not streamed) and incur less memory
overhead since no intermediate objects will be created.

PR-URL: #51044
Refs: nodejs/performance#136
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
richardlau added a commit that referenced this pull request Mar 25, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) #51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) #51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) #51794
doc:
  * add lemire to collaborators (Daniel Lemire) #51572
  * add zcbenz to collaborators (Cheng Zhao) #51812
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) #51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) #50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) #50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) #50960
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) #51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) #51244

PR-URL: #52212
@richardlau richardlau mentioned this pull request Mar 25, 2024
rdw-msft pushed a commit to rdw-msft/node that referenced this pull request Mar 26, 2024
PR-URL: nodejs#51044
Refs: nodejs/performance#136
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
rdw-msft pushed a commit to rdw-msft/node that referenced this pull request Mar 26, 2024
This patch introduces a helper crypto.hash() that computes
a digest from the input at one shot. This can be 1.2-1.6x faster
than the object-based createHash() for smaller inputs (<= 5MB)
that are readily available (not streamed) and incur less memory
overhead since no intermediate objects will be created.

PR-URL: nodejs#51044
Refs: nodejs/performance#136
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
rdw-msft pushed a commit to rdw-msft/node that referenced this pull request Mar 26, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) nodejs#51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) nodejs#51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) nodejs#51794
doc:
  * add zcbenz to collaborators (Cheng Zhao) nodejs#51812
  * add lemire to collaborators (Daniel Lemire) nodejs#51572
http2:
  * (SEMVER-MINOR) add h2 compat support for appendHeader (Tim Perry) nodejs#51412
  * (SEMVER-MINOR) add server handshake utility (snek) nodejs#51172
  * (SEMVER-MINOR) receive customsettings (Marten Richter) nodejs#51323
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) nodejs#51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) nodejs#50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) nodejs#50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) nodejs#50960
  * (SEMVER-MINOR) support multi-line values for .env file (IlyasShabi) nodejs#51289
  * (SEMVER-MINOR) add `process.loadEnvFile` and `util.parseEnv` (Yagiz Nizipli) nodejs#51476
  * (SEMVER-MINOR) do not coerce dotenv paths (Tobias Nießen) nodejs#51425
stream:
  * (SEMVER-MINOR) implement `min` option for `ReadableStreamBYOBReader.read` (Mattias Buelens) nodejs#50888
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) nodejs#51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) nodejs#51244

PR-URL: nodejs#51932
richardlau added a commit that referenced this pull request Mar 26, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) #51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) #51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) #51794
doc:
  * add lemire to collaborators (Daniel Lemire) #51572
  * add zcbenz to collaborators (Cheng Zhao) #51812
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) #51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) #50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) #50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) #50960
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) #51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) #51244

PR-URL: #52212
@GauriSpears
Copy link
Contributor

GauriSpears commented Mar 27, 2024

It looks like this commit broke my code. I use algorithm from external OpenSSL engine and now I get error:0308010C:digital envelope routines::unsupported
I suppose you added some check that algorithm exists but forgot that it may be loaded by crypto.setEngine and so it's not in the digests list of statically linked OpenSSL.

@joyeecheung
Copy link
Member Author

@GauriSpears I think that should be caused by #51034 - can you open an issue with a reproduction?

@GauriSpears
Copy link
Contributor

GauriSpears commented Mar 27, 2024

@joyeecheung , you are right. Not all of digest algos can be retrieved via EVP_MD_fetch and EVP_get_digestbyname should be used to get them. I've fixed the same problem in OpenSSL last year:
openssl/openssl#20651

@joyeecheung
Copy link
Member Author

Not all of digest algos can be retrieved via EVP_MD_fetch and EVP_get_digestbyname should be used to get them.

The caching does EVP_get_digestbyname first and if EVP_MD_fetch doesn't return a result, it uses the one returned by EVP_get_digestbyname, otherwise it uses the one fetched by EVP_MD_fetch, so it should work as long as EVP_get_digestbyname provides valid results. We also have some tests for setEngine, but not a lot. If the caching somehow broke your use case, can you provide a reproduction?

@karlhorky
Copy link

karlhorky commented Mar 28, 2024

This is great, thanks @joyeecheung and other contributors!! 🙌

For TypeScript users, @types/node will need a PR to DefinitelyTyped to add crypto.hash():

TypeScript Playground

Error message:

Property 'hash' does not exist on type 'typeof import("node:crypto")'. Did you mean 'Hash'?(2551)

cc DT maintainers / contributors to @types/node crypto @Semigradsky @jhmaster2000


Edit: this has been added in @types/node@20.12.8 by this PR by @codershiba:

@GauriSpears
Copy link
Contributor

The caching does EVP_get_digestbyname first and if EVP_MD_fetch doesn't return a result, it uses the one returned by EVP_get_digestbyname

I'm trying to dig into your code. From the first sight it seems really equivalent to what happens in OpenSSL. Could you explain: when the caching of algorithm list is done? And how it shoud work if setEngine loads engine with new algorithms?

@joyeecheung
Copy link
Member Author

joyeecheung commented Mar 30, 2024

Could you explain: when the caching of algorithm list is done?

That was explained in #51034 (comment)

And how it shoud work if setEngine loads engine with new algorithms?

It's supposed to keep trying EVP_get_digestbyname when there's no cached entry for that name (i.e. when it's not explicitly fetchable via EVP_MD_fetch).

It seems a bit off topic for this PR. Can you open a separate issue for this? Ideally with a reproduction.

jcbhmr pushed a commit to jcbhmr/node that referenced this pull request May 15, 2024
Notable changes:

build:
  * (SEMVER-MINOR) build opt to set local location of headers (Michael Dawson) nodejs#51525
crypto:
  * (SEMVER-MINOR) implement crypto.hash() (Joyee Cheung) nodejs#51044
  * update root certificates to NSS 3.98 (Node.js GitHub Bot) nodejs#51794
doc:
  * add zcbenz to collaborators (Cheng Zhao) nodejs#51812
  * add lemire to collaborators (Daniel Lemire) nodejs#51572
http2:
  * (SEMVER-MINOR) add h2 compat support for appendHeader (Tim Perry) nodejs#51412
  * (SEMVER-MINOR) add server handshake utility (snek) nodejs#51172
  * (SEMVER-MINOR) receive customsettings (Marten Richter) nodejs#51323
lib:
  * (SEMVER-MINOR) move encodingsMap to internal/util (Joyee Cheung) nodejs#51044
sea:
  * (SEMVER-MINOR) support sea.getRawAsset() (Joyee Cheung) nodejs#50960
  * (SEMVER-MINOR) support embedding assets (Joyee Cheung) nodejs#50960
src:
  * (SEMVER-MINOR) print string content better in BlobDeserializer (Joyee Cheung) nodejs#50960
  * (SEMVER-MINOR) support multi-line values for .env file (IlyasShabi) nodejs#51289
  * (SEMVER-MINOR) add `process.loadEnvFile` and `util.parseEnv` (Yagiz Nizipli) nodejs#51476
  * (SEMVER-MINOR) do not coerce dotenv paths (Tobias Nießen) nodejs#51425
stream:
  * (SEMVER-MINOR) implement `min` option for `ReadableStreamBYOBReader.read` (Mattias Buelens) nodejs#50888
util:
  * (SEMVER-MINOR) add styleText API to text formatting (Rafael Gonzaga) nodejs#51850
vm:
  * (SEMVER-MINOR) support using the default loader to handle dynamic import() (Joyee Cheung) nodejs#51244

PR-URL: nodejs#51932
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. notable-change PRs with changes that should be highlighted in changelogs. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.