diff options
Diffstat (limited to 'test/js/third_party/jsonwebtoken/validateAsymmetricKey.test.js')
-rw-r--r-- | test/js/third_party/jsonwebtoken/validateAsymmetricKey.test.js | 209 |
1 files changed, 209 insertions, 0 deletions
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(); + }); + }); + } +}); |