Skip to content

crypto: support outputLength in XOF hash functions #58121

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Aditi-1400
Copy link
Contributor

Support outputLength option in crypto.hash() for XOF hash functions to align with the behaviour of crypto.createHash() API

Fixes: #57312

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/security-wg

@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 May 2, 2025
@panva panva requested review from tniessen and joyeecheung May 2, 2025 14:26
@panva panva added the crypto Issues and PRs related to the crypto subsystem. label May 2, 2025
Copy link
Member

@panva panva left a comment

Choose a reason for hiding this comment

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

@Aditi-1400 please change the PR title and commit message to crypto: support outputLength in crypto.hash for XOF functions

@Aditi-1400 Aditi-1400 changed the title src: support outputLength in XOF hash functions crypto: support outputLength in XOF hash functions May 2, 2025
Copy link

codecov bot commented May 2, 2025

Codecov Report

Attention: Patch coverage is 73.33333% with 12 lines in your changes missing coverage. Please review.

Project coverage is 90.21%. Comparing base (a4c7c9f) to head (b9379c5).
Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
src/crypto/crypto_hash.cc 54.16% 8 Missing and 3 partials ⚠️
lib/internal/crypto/hash.js 95.23% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #58121      +/-   ##
==========================================
+ Coverage   90.20%   90.21%   +0.01%     
==========================================
  Files         635      635              
  Lines      187616   187635      +19     
  Branches    36847    36865      +18     
==========================================
+ Hits       169234   169276      +42     
+ Misses      11150    11147       -3     
+ Partials     7232     7212      -20     
Files with missing lines Coverage Δ
lib/internal/crypto/hash.js 98.36% <95.23%> (-0.31%) ⬇️
src/crypto/crypto_hash.cc 70.95% <54.16%> (-2.06%) ⬇️

... and 30 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

DataPointer xofHashDigest(const Buffer<const unsigned char>& buf,
const EVP_MD* md,
size_t output_length) {
if (md == nullptr) return {};
Copy link
Member

Choose a reason for hiding this comment

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

@codebytere ... can I ask you to take a look at this from a Whether-This-Will-Work-In-BoringSSL perspective?

size_t output_length) {
if (md == nullptr) return {};

EVP_MD_CTX* ctx = EVP_MD_CTX_new();
Copy link
Member

Choose a reason for hiding this comment

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

This should use EVPMDCtxPointer

@@ -280,6 +280,9 @@ class Digest final {

DataPointer hashDigest(const Buffer<const unsigned char>& data,
const EVP_MD* md);
DataPointer xofHashDigest(const Buffer<const unsigned char>& data,
const EVP_MD* md,
size_t length);
Copy link
Member

Choose a reason for hiding this comment

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

This could use a code comment to describe the differences between hashDigest(...) and xosHashDigest(...)

@@ -4187,7 +4187,7 @@ A convenient alias for [`crypto.webcrypto.getRandomValues()`][]. This
implementation is not compliant with the Web Crypto spec, to write
web-compatible code use [`crypto.webcrypto.getRandomValues()`][] instead.

### `crypto.hash(algorithm, data[, outputEncoding])`
### `crypto.hash(algorithm, data[, outputEncoding, outputLength])`
Copy link
Member

Choose a reason for hiding this comment

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

I think for this I'd rather we support an alternative signature that accepts either an encoding for outputEncoding or an options object... e.g.

// current signature
crypto.hash(algorithm, data[, outputEncoding]);

crypto.hash(algorithm, data[, options]);
// where `options` is:
// { outputEncoding: '...', outputLength: ... }

Makes things more scalable in case additional options need to be added later.

Copy link
Contributor Author

@Aditi-1400 Aditi-1400 May 12, 2025

Choose a reason for hiding this comment

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

I am not sure if we wanted to support null, for now I've kept the same behaviour as

function getOptions(options, defaultOptions = kEmptyObject) {
.
Also, crypto.createHash() just ignores all the invalid options, so let me know which behaviour we should have.

Copy link
Member

Choose a reason for hiding this comment

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

Not supporting null is fine, it should be either a string or an object (we have many overload like this e.g. in buffer)

Comment on lines 252 to 263
int output_length = args[6].As<Uint32>()->Value();
if (isXOF) {
return ncrypto::xofHashDigest(buf, md, output_length);
} else if (!isXOF && output_length != EVP_MD_get_size(md)) {
ThrowCryptoError(env, ERR_get_error(), "Invalid length or not XOF");
return {};
}
Copy link
Member

@joyeecheung joyeecheung May 28, 2025

Choose a reason for hiding this comment

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

Suggested change
int output_length = args[6].As<Uint32>()->Value();
if (isXOF) {
return ncrypto::xofHashDigest(buf, md, output_length);
} else if (!isXOF && output_length != EVP_MD_get_size(md)) {
ThrowCryptoError(env, ERR_get_error(), "Invalid length or not XOF");
return {};
}
int output_length = args[6].As<Uint32>()->Value();
if (isXOF) {
return ncrypto::xofHashDigest(buf, md, output_length);
}
if (output_length != EVP_MD_get_size(md)) {
Utf8Value method(isolate, args[0]);
std::string message = "Output length " + std::to_string(output_length) + " is invalid for ";
message += method.ToString() + ", which does not support XOF";
ThrowCryptoError(env, ERR_get_error(), message.c_str());
return {};
}
// Does not support XOF but output length matches expected digest size.
// Compute digest as usual.

Support `outputLength` option in crypto.hash() for XOF hash
functions to align with the behaviour of crypto.createHash()
API
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crypto Issues and PRs related to the crypto subsystem. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

crypto.hash XOF hash functions outputLength option
5 participants