Repository navigation
Restore Node.js sign/verify key-option, digest and temporary-prefix semantics #7613
Description
node:crypto / node:fs — four Node.js compatibility gaps in sign/verify and mkdtemp
Four Node.js compatibility defects were reproduced at upstream commit
535b64a124df7fb3108b904a75287470c7a4ab62, using Node as the independent
control. The default-digest, wrapped-key and temporary-prefix defects block
an unmodified tuf-js 6.0.0 / @sigstore/tuf 5.0.0 client. Native regressions
and separate Node-generated signature tests also exposed incorrect RSA
padding and salt-length handling. The RSA tests are separate from the TUF
client test. Related work: #6924 (Node compatibility survey) and #4269
(the earlier mkdtemp implementation).
1. sign/verify with an omitted or null digest fails for RSA/EC keys
verify(null, data, ecPublicKey, sig); // Node: verifies as sha256
// workerd: Error: Failed to initialize verification contextNode derives the digest from the key type when algorithm is omitted or
null (SHA-256 for RSA/EC; Ed25519/Ed448 are digest-free). workerd passed the
absent digest straight into mdctx.signInit/verifyInit, which fail.
2. options.key does not accept a KeyObject or CryptoKey
sign('sha256', data, { key: privateKeyObject }); // Node: works
// workerd: "options.key" property must be of type string or ... Received
// an instance of PrivateKeyObjectNode unwraps a KeyObject/CryptoKey supplied inside the options object —
this is the form tuf-js uses, so its verifyDelegate path reported
"root was signed by 0/3 keys". workerd's key parser only handled raw
string/Buffer material inside options.key.
3. One-shot RSA sign/verify ignores padding and saltLength
const sig = sign('sha256', data, { key, padding: RSA_PKCS1_PSS_PADDING, saltLength: 32 });
verify('sha256', data, { key: pub, padding: RSA_PKCS1_PSS_PADDING, saltLength: 32 }, sig);
// Node: true; verify with default padding: false
// workerd: the signature was PKCS1 (padding ignored); every padding option verified trueCryptoImpl::signOneShot/verifyOneShot accepted rsaPadding/
pssSaltLength but never applied them to the operation context, so one-shot
RSA always used the default padding. Node cross-check (Node-made signatures
verified in workerd): a real PSS signature was rejected under correct PSS
options while a PKCS1 signature was accepted under PSS options.
4. fs.mkdtemp('/tmp/') throws EINVAL when the prefix names an existing directory
fs.mkdtempSync('/tmp/'); // Node: '/tmp/<random>'
// workerd: EINVAL inside mkdtempSyncThe mkdir path returned early when the prefix itself already exists instead
of creating <prefix><generated>.
Scope
The proposed fix covers the four defects and their maintained regressions
in one linear branch. Preserve explicit digest behavior, key type and key
creation restrictions, signature options, and filesystem write restrictions.
Completion requires the full affected native targets, independent Node
signature comparisons, and the unchanged TUF metadata-client test to pass.
No changes outside src/node//src/workerd/api/node/ —
crypto_keys.ts, crypto_sign.ts, crypto.c++, filesystem.c++ plus
regression tests.
Activity