From 35109160ca5d439116bedeb3302ec3745e2895d5 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari Date: Sat, 7 Oct 2023 19:22:45 -0300 Subject: feat(KeyObject) (#5940) * oops * createSecretKey but weird error * use the right prototype, do not add a function called export lol * HMAC JWT export + base64 fix * Fix Equals, Fix Get KeySize, add complete export RSA * fix RSA export * add EC exports * X25519 and ED25519 export + fixes * fix default exports * better asymmetricKeyType * fix private exports * fix symmetricKeySize * createPublicKey validations + refactor * jwt + der fixes * oopsies * add PEM into createPublicKey * cleanup * WIP * bunch of fixes * public from private + private OKP * encrypted keys fixes * oops * fix clear tls error, add some support to jwk and other formats on publicEncrypt/publicDecrypt * more fixes and tests working * more fixes more tests * more clear hmac errors * more tests and fixes * add generateKeyPair * more tests passing, some skips * fix EC key from private * fix OKP JWK * nodejs ignores ext and key_ops on KeyObject.exports * add EC sign verify test * some fixes * add crypto.generateKeyPairSync(type, options) * more fixes and more tests * fix hmac tests * jsonwebtoken tests * oops * oops2 * generated files * revert package.json * vm tests * todos instead of failues * toBunString -> toString * undo simdutf * improvements * unlikely * cleanup * cleanup 2 * oops * move _generateKeyPairSync checks to native --- .../jsonwebtoken/validateAsymmetricKey.test.js | 209 +++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 test/js/third_party/jsonwebtoken/validateAsymmetricKey.test.js (limited to 'test/js/third_party/jsonwebtoken/validateAsymmetricKey.test.js') diff --git a/test/js/third_party/jsonwebtoken/validateAsymmetricKey.test.js b/test/js/third_party/jsonwebtoken/validateAsymmetricKey.test.js new file mode 100644 index 000000000..4bcf13cb0 --- /dev/null +++ b/test/js/third_party/jsonwebtoken/validateAsymmetricKey.test.js @@ -0,0 +1,209 @@ +import { expect, describe, it } from "bun:test"; +import { createPrivateKey } from "crypto"; +import fs from "fs"; +import path from "path"; +const PS_SUPPORTED = true; +const ASYMMETRIC_KEY_DETAILS_SUPPORTED = true; +const RSA_PSS_KEY_DETAILS_SUPPORTED = true; +const allowedAlgorithmsForKeys = { + "ec": ["ES256", "ES384", "ES512"], + "rsa": ["RS256", "PS256", "RS384", "PS384", "RS512", "PS512"], + "rsa-pss": ["PS256", "PS384", "PS512"], +}; + +const allowedCurves = { + ES256: "prime256v1", + ES384: "secp384r1", + ES512: "secp521r1", +}; + +function validateAsymmetricKey(algorithm, key) { + if (!algorithm || !key) return; + + const keyType = key.asymmetricKeyType; + if (!keyType) return; + + const allowedAlgorithms = allowedAlgorithmsForKeys[keyType]; + + if (!allowedAlgorithms) { + throw new Error(`Unknown key type "${keyType}".`); + } + + if (!allowedAlgorithms.includes(algorithm)) { + throw new Error(`"alg" parameter for "${keyType}" key type must be one of: ${allowedAlgorithms.join(", ")}.`); + } + + /* + * Ignore the next block from test coverage because it gets executed + * conditionally depending on the Node version. Not ignoring it would + * prevent us from reaching the target % of coverage for versions of + * Node under 15.7.0. + */ + /* istanbul ignore next */ + if (ASYMMETRIC_KEY_DETAILS_SUPPORTED) { + switch (keyType) { + case "ec": + const keyCurve = key.asymmetricKeyDetails.namedCurve; + const allowedCurve = allowedCurves[algorithm]; + + if (keyCurve !== allowedCurve) { + throw new Error(`"alg" parameter "${algorithm}" requires curve "${allowedCurve}".`); + } + break; + + case "rsa-pss": + if (RSA_PSS_KEY_DETAILS_SUPPORTED) { + const length = parseInt(algorithm.slice(-3), 10); + const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = key.asymmetricKeyDetails; + + if (hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm) { + throw new Error( + `Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${algorithm}.`, + ); + } + + if (saltLength !== undefined && saltLength > length >> 3) { + throw new Error( + `Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${algorithm}.`, + ); + } + } + break; + } + } +} + +function loadKey(filename) { + return createPrivateKey(fs.readFileSync(path.join(__dirname, filename))); +} + +const algorithmParams = { + RS256: { + invalidPrivateKey: loadKey("secp384r1-private.pem"), + }, + ES256: { + invalidPrivateKey: loadKey("priv.pem"), + }, +}; + +if (PS_SUPPORTED) { + algorithmParams.PS256 = { + invalidPrivateKey: loadKey("secp384r1-private.pem"), + }; +} + +describe("Asymmetric key validation", function () { + Object.keys(algorithmParams).forEach(function (algorithm) { + describe(algorithm, function () { + const keys = algorithmParams[algorithm]; + + describe("when validating a key with an invalid private key type", function () { + it("should throw an error", function () { + const expectedErrorMessage = /"alg" parameter for "[\w\d-]+" key type must be one of:/; + + expect(function () { + validateAsymmetricKey(algorithm, keys.invalidPrivateKey); + }).toThrow(expectedErrorMessage); + }); + }); + }); + }); + + describe("when the function has missing parameters", function () { + it("should pass the validation if no key has been provided", function () { + const algorithm = "ES256"; + validateAsymmetricKey(algorithm); + }); + + it.todo("should pass the validation if no algorithm has been provided", function () { + const key = loadKey("dsa-private.pem"); + validateAsymmetricKey(null, key); + }); + }); + + describe("when validating a key with an unsupported type", function () { + it.todo("should throw an error", function () { + const algorithm = "RS256"; + const key = loadKey("dsa-private.pem"); + const expectedErrorMessage = 'Unknown key type "dsa".'; + + expect(function () { + validateAsymmetricKey(algorithm, key); + }).toThrow(expectedErrorMessage); + }); + }); + + describe("Elliptic curve algorithms", function () { + const curvesAlgorithms = [ + { algorithm: "ES256", curve: "prime256v1" }, + { algorithm: "ES384", curve: "secp384r1" }, + { algorithm: "ES512", curve: "secp521r1" }, + ]; + + const curvesKeys = [ + { curve: "prime256v1", key: loadKey("prime256v1-private.pem") }, + { curve: "secp384r1", key: loadKey("secp384r1-private.pem") }, + { curve: "secp521r1", key: loadKey("secp521r1-private.pem") }, + ]; + + describe("when validating keys generated using Elliptic Curves", function () { + curvesAlgorithms.forEach(function (curveAlgorithm) { + curvesKeys.forEach(curveKeys => { + if (curveKeys.curve !== curveAlgorithm.curve) { + if (ASYMMETRIC_KEY_DETAILS_SUPPORTED) { + it(`should throw an error when validating an ${curveAlgorithm.algorithm} token for key with curve ${curveKeys.curve}`, function () { + expect(() => { + validateAsymmetricKey(curveAlgorithm.algorithm, curveKeys.key); + }).toThrow(`"alg" parameter "${curveAlgorithm.algorithm}" requires curve "${curveAlgorithm.curve}".`); + }); + } else { + it(`should pass the validation for incorrect keys if the Node version does not support checking the key's curve name`, function () { + expect(() => { + validateAsymmetricKey(curveAlgorithm.algorithm, curveKeys.key); + }).not.toThrow(); + }); + } + } else { + it(`should accept an ${curveAlgorithm.algorithm} token for key with curve ${curveKeys.curve}`, function () { + expect(() => { + validateAsymmetricKey(curveAlgorithm.algorithm, curveKeys.key); + }).not.toThrow(); + }); + } + }); + }); + }); + }); + + if (RSA_PSS_KEY_DETAILS_SUPPORTED) { + describe.todo("RSA-PSS algorithms", function () { + // const key = loadKey('rsa-pss-private.pem'); + + it(`it should throw an error when validating a key with wrong RSA-RSS parameters`, function () { + const algorithm = "PS512"; + expect(function () { + validateAsymmetricKey(algorithm, key); + }).toThrow( + 'Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" PS512', + ); + }); + + it(`it should throw an error when validating a key with invalid salt length`, function () { + const algorithm = "PS256"; + const shortSaltKey = loadKey("rsa-pss-invalid-salt-length-private.pem"); + expect(function () { + validateAsymmetricKey(algorithm, shortSaltKey); + }).toThrow( + 'Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" PS256.', + ); + }); + + it(`it should pass the validation when the key matches all the requirements for the algorithm`, function () { + expect(function () { + const algorithm = "PS256"; + validateAsymmetricKey(algorithm, key); + }).not.toThrow(); + }); + }); + } +}); -- cgit v1.2.3