diff --git a/src/jsc/bindings/node/crypto/JSCipher.cpp b/src/jsc/bindings/node/crypto/JSCipher.cpp index 716828c4d471..468bb5f2293c 100644 --- a/src/jsc/bindings/node/crypto/JSCipher.cpp +++ b/src/jsc/bindings/node/crypto/JSCipher.cpp @@ -84,7 +84,8 @@ JSValue rsaFunction(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* ca if (JSObject* options = optionsValue.getObject()) { JSValue paddingValue = options->get(lexicalGlobalObject, Identifier::fromString(vm, "padding"_s)); RETURN_IF_EXCEPTION(scope, {}); - if (!paddingValue.isUndefined()) { + // Node reads `key.padding || defaultPadding`: https://github.com/nodejs/node/blob/v26.3.0/lib/internal/crypto/cipher.js#L72 + if (paddingValue.toBoolean(lexicalGlobalObject)) { padding = paddingValue.toInt32(lexicalGlobalObject); RETURN_IF_EXCEPTION(scope, {}); } diff --git a/test/js/node/crypto/crypto-rsa.test.js b/test/js/node/crypto/crypto-rsa.test.js index 3d360b096e25..a531f99c6010 100644 --- a/test/js/node/crypto/crypto-rsa.test.js +++ b/test/js/node/crypto/crypto-rsa.test.js @@ -409,3 +409,48 @@ describe("Invalid oaepHash and oaepLabel options", () => { }); }); }); + +// Node reads the option as `key.padding || defaultPadding`: https://github.com/nodejs/node/blob/v26.3.0/lib/internal/crypto/cipher.js#L72 +describe("padding option", () => { + const message = Buffer.from("I AM THE WALRUS"); + let signed; // privateEncrypt with the default RSA_PKCS1_PADDING: deterministic + let sealed; // publicEncrypt with the default RSA_PKCS1_OAEP_PADDING: randomized + + beforeAll(() => { + signed = crypto.privateEncrypt(rsaKeyPem, message); + sealed = crypto.publicEncrypt(rsaPubPem, message); + }); + + test.each([ + ["null", null], + ["0", 0], + ["-0", -0], + ["false", false], + ["NaN", NaN], + ["an empty string", ""], + ["0n", 0n], + ])("%s means the default padding", (_, padding) => { + expect(crypto.privateEncrypt({ key: rsaKeyPem, padding }, message)).toEqual(signed); + expect(crypto.publicDecrypt({ key: rsaPubPem, padding }, signed)).toEqual(message); + expect(crypto.privateDecrypt(rsaKeyPem, crypto.publicEncrypt({ key: rsaPubPem, padding }, message))).toEqual( + message, + ); + expect(crypto.privateDecrypt({ key: rsaKeyPem, padding }, sealed)).toEqual(message); + }); + + // A truthy value is converted to an integer, and 0 is not a padding mode. + test.each([ + ["'0'", "0"], + ["{}", {}], + ["[]", []], + ])("%s is not a padding mode", (_, padding) => { + const paddingError = expect.objectContaining({ + name: "Error", + code: "ERR_OSSL_EVP_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE", + }); + expect(() => crypto.privateEncrypt({ key: rsaKeyPem, padding }, message)).toThrow(paddingError); + expect(() => crypto.publicDecrypt({ key: rsaPubPem, padding }, signed)).toThrow(paddingError); + expect(() => crypto.publicEncrypt({ key: rsaPubPem, padding }, message)).toThrow(paddingError); + expect(() => crypto.privateDecrypt({ key: rsaKeyPem, padding }, sealed)).toThrow(paddingError); + }); +});