From 399fbbc315cd1c603bd06ab269cd253c387b72cb Mon Sep 17 00:00:00 2001 From: Carlos Lopez Jr Date: Mon, 27 Feb 2023 16:43:23 -0500 Subject: [PATCH] test: wrap for deno testing --- test/lib/ACMEAgent.js | 3 +- test/lib/jwe.js | 3 +- test/lib/jwk.js | 3 +- test/lib/jws.js | 86 +++++++++++++++++++++++++++++- test/sanity.js | 2 +- test/tester.js | 83 +++++++++++++++++++++++++++++ test/utils/asn1.js | 5 +- test/utils/base64.js | 3 +- test/utils/certificate.js | 40 ++++++++++++++ test/utils/crypto.js | 109 +++++++++++++++++--------------------- test/utils/pkcs.js | 18 ------- utils/asn1/encoder.js | 7 ++- 12 files changed, 267 insertions(+), 95 deletions(-) create mode 100644 test/tester.js create mode 100644 test/utils/certificate.js delete mode 100644 test/utils/pkcs.js diff --git a/test/lib/ACMEAgent.js b/test/lib/ACMEAgent.js index 85e3712..99c9e14 100644 --- a/test/lib/ACMEAgent.js +++ b/test/lib/ACMEAgent.js @@ -1,6 +1,5 @@ -import test from 'ava'; - import Agent from '../../lib/ACMEAgent.js'; +import test from '../tester.js'; const LETS_ENCRYPT_STAGE_URL = 'https://acme-staging-v02.api.letsencrypt.org/directory'; diff --git a/test/lib/jwe.js b/test/lib/jwe.js index a919ea1..ed62f36 100644 --- a/test/lib/jwe.js +++ b/test/lib/jwe.js @@ -1,6 +1,5 @@ -import test from 'ava'; - import { encodeBase64UrlAsString } from '../../utils/base64.js'; +import test from '../tester.js'; test('rfc7516 A.1.1', (t) => { const protectedHeader = { alg: 'RSA-OAEP', enc: 'A256GCM' }; diff --git a/test/lib/jwk.js b/test/lib/jwk.js index abef3bb..fa8dc78 100644 --- a/test/lib/jwk.js +++ b/test/lib/jwk.js @@ -1,6 +1,5 @@ -import test from 'ava'; - import { encodeBase64UrlAsString } from '../../utils/base64.js'; +import test from '../tester.js'; test('rfc7517 A.1.1', (t) => { const protectedHeader = { alg: 'RSA-OAEP', enc: 'A256GCM' }; diff --git a/test/lib/jws.js b/test/lib/jws.js index 86f825f..b8b05b7 100644 --- a/test/lib/jws.js +++ b/test/lib/jws.js @@ -1,8 +1,8 @@ -import test from 'ava'; - import { createES256JWK, createES512JWK, createHS256JWK, createRS256JWK } from '../../lib/jwa.js'; import { decodeJSON, decodeJSONUnsafe, decodeString, decodeStringUnsafe, decodeUint8Array, decodeUint8ArrayUnsafe, signCompact, signObject, validate } from '../../lib/jws.js'; import { decodeBase64AsString, decodeBase64UrlAsArray, encodeBase64UrlAsString } from '../../utils/base64.js'; +import { octetFromUtf8 } from '../../utils/utf8.js'; +import test from '../tester.js'; const RFC7515_APPENDIX_A_JWS = 'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; const RFC7515_PAYLOAD = '{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}'; @@ -338,3 +338,85 @@ test('decode unsafe JSON', async (t) => { }); t.deepEqual(payload, decodeJSONUnsafe(jws)); }); + +// https://www.rfc-editor.org/rfc/rfc8725.html#CVE-2015-9235 +test('CVE-2015-9235 - Replicate setup', async (t) => { + const header = { alg: 'HS256', typ: 'JWT' }; + const payload = { loggedInAs: 'admin', iat: 1_422_779_638 }; + const key = 'secretkey'; + const hmacKey = new Uint8Array(octetFromUtf8(key)); + const symmetricalJWK = createHS256JWK({ k: hmacKey }); + const symmetricalJWS = await signCompact({ + protected: header, + payload, + jwk: symmetricalJWK, + }); + t.deepEqual(symmetricalJWS, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI'); + + t.deepEqual(await validate(symmetricalJWS, symmetricalJWK), 'eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9'); +}); + +test('CVE-2015-9235 - Validation should fail if token specifies alg:none though JWK used', async (t) => { + const header = { alg: 'none', typ: 'JWT' }; + const payload = { loggedInAs: 'admin', iat: 1_422_779_638 }; + const key = 'secretkey'; + const hmacKey = new Uint8Array(octetFromUtf8(key)); + const symmetricalJWK = createHS256JWK({ k: hmacKey }); + + // Will not allow conflict when signing + await t.throwsAsync(signCompact({ + protected: header, + payload, + jwk: symmetricalJWK, + })); + + const noneJWS = await signCompact({ + protected: header, + payload, + jwk: null, + }); + + t.deepEqual(noneJWS, 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.'); + + t.deepEqual(await validate(noneJWS, null), 'eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9'); + await t.throwsAsync(validate(noneJWS, symmetricalJWK)); +}); + +test('CVE-2015-9235 - Tokens headers do not override key algorithm when validating', async (t) => { + const header = { alg: 'HS256', typ: 'JWT' }; + const payload = { loggedInAs: 'admin', iat: 1_422_779_638 }; + const publicKey = 'ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ'; + const symmetricalJWK = createHS256JWK({ k: publicKey }); + const symmetricalJWS = await signCompact({ + protected: header, + payload, + jwk: symmetricalJWK, + }); + t.deepEqual(symmetricalJWS, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.OE2MlSdwscv4nwTVGpT8K8DqObPUcNHSMVoZ_e_YX3U'); + + // Create Private JWK that has matching Public Key as previous HMAC + const asymmetricalJWK = createRS256JWK({ + n: publicKey, + e: 'AQAB', + d: 'Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ', + p: '4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc', + q: 'uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc', + dp: 'BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0', + dq: 'h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU', + qi: 'IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U', + }); + + const asymmetricalJWS = await signCompact({ + protected: header, + payload, + jwk: asymmetricalJWK, + }); + + t.deepEqual(asymmetricalJWS, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.jqp5oj5ByCAWCp8yQB1gCOjt3fBFEC9aQOC47Nrm_8D66G6syVupXQS6ym9QB-490Gj0K3ve5LmdLhLsNBQhacCCoH-TNmVD8hElcCbeS2g_moKjfl5TZ6fkDneptIZdlNW0HMY5frrkmQ0W6Jei3VKtOYsEX8DfLKZzG3FV2ODs5Istm2x7ObkbqVkOQsPpB15h1-x61VDTMLnL6TeOLVCiVX83Tp9YklLtDySZeQgfU7XhFB969OxMx49_zkZG_TKnctuxbH8kGOIKnyy7YTU1ecURhK_YqkTR8Z4OLoltEZxgSycIHmJx-CwQs4nHeU4igiE8OBUteX6e8uQz3w'); + + // Keys only validate if match + t.deepEqual(await validate(symmetricalJWS, symmetricalJWK), 'eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9'); + t.deepEqual(await validate(asymmetricalJWS, asymmetricalJWK), 'eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9'); + await t.throwsAsync(validate(symmetricalJWS, asymmetricalJWK)); + await t.throwsAsync(validate(asymmetricalJWS, symmetricalJWK)); +}); diff --git a/test/sanity.js b/test/sanity.js index 1ee358a..5682f36 100644 --- a/test/sanity.js +++ b/test/sanity.js @@ -1,4 +1,4 @@ -import test from 'ava'; +import test from './tester.js'; test('sync test', (t) => { t.pass(); diff --git a/test/tester.js b/test/tester.js new file mode 100644 index 0000000..d00caf9 --- /dev/null +++ b/test/tester.js @@ -0,0 +1,83 @@ +/** @typedef {import('ava').TestFn} AvaTestFn */ + +// @ts-ignore Custom cast +export default await (async () => { + if ('Deno' in globalThis) { + const denoAsserts = await import('https://deno.land/std@0.178.0/testing/asserts.ts'); + /** + * + * @param {string} title + * @param {import('ava').ImplementationFn} implementation + */ + // eslint-disable-next-line no-inner-declarations + function test(title, implementation) { + Deno.test(title, async (t) => { + /** @type {import('ava').ExecutionContext} T */ + const wrapper = { + assert: denoAsserts.assertStrictEquals, + deepEqual: denoAsserts.assertEquals, + // like + fail: denoAsserts.fail, + false(actual, message) { + denoAsserts.assertStrictEquals(actual, false, message); + return true; + }, + falsy(actual, message) { + denoAsserts.assertStrictEquals(!actual, true, message); + return true; + }, + is: denoAsserts.assertStrictEquals, + not(actual, expected) { + denoAsserts.assertStrictEquals(!Object.is(actual, expected), true); + }, + notDeepEqual: denoAsserts.assertNotEquals, + notRegex: denoAsserts.notMatch, + notThrows(fn, message) { + try { + fn(); + } catch { + this.fail(message); + } + }, + async notThrowsAsync(fn, message) { + try { + await fn(); + } catch { + this.fail(message); + } + }, + pass(message) { + denoAsserts.assert(true, message); + }, + regex: denoAsserts.assertMatch, + // snapshot + throws: denoAsserts.assertThrows, + async throwsAsync(fn, expectations, message) { + try { + await fn(); + this.fail(message); + } catch (e) { + this.throws(() => { throw e; }, expectations, message); + } + }, + true(actual, message) { + denoAsserts.assertStrictEquals(actual, true, message); + }, + truthy(actual, message) { + denoAsserts.assert(actual, message); + }, + log: console.debug, + }; + await implementation(wrapper); + }); + } + return test; + } + if ('window' in globalThis) { + // TODO: Tap into browser-based tester + return null; + } + // Node + const ava = await import('ava'); + return ava.default; +})(); diff --git a/test/utils/asn1.js b/test/utils/asn1.js index 9232a90..1ef1d84 100644 --- a/test/utils/asn1.js +++ b/test/utils/asn1.js @@ -1,7 +1,6 @@ -import test from 'ava'; - import { decodeDER, readLength, readObjectIdentifier, readSignedNumber, readUnsignedNumber, writeLength, writeSignedNumber, writeUnsignedNumber } from '../../utils/asn1.js'; -import { derFromPEM } from '../../utils/pkcs.js'; +import { derFromPEM } from '../../utils/certificate.js'; +import test from '../tester.js'; test('writeUnsignedNumber()', (t) => { t.deepEqual(writeUnsignedNumber(1), [1]); diff --git a/test/utils/base64.js b/test/utils/base64.js index a179ff3..7abfcd4 100644 --- a/test/utils/base64.js +++ b/test/utils/base64.js @@ -1,6 +1,5 @@ -import test from 'ava'; - import { decodeBase64AsArray, decodeBase64AsUtf8, encodeBase64AsArray, encodeBase64AsString } from '../../utils/base64.js'; +import test from '../tester.js'; const textEncoder = new TextEncoder(); diff --git a/test/utils/certificate.js b/test/utils/certificate.js new file mode 100644 index 0000000..7a889dc --- /dev/null +++ b/test/utils/certificate.js @@ -0,0 +1,40 @@ +import { formatPEM, pemFromPKCS1, pemFromPKCS8, pkcs1FromPKCS8, pkcs8FromPKCS1 } from '../../utils/certificate.js'; +import test from '../tester.js'; + +const FIXTURES = { + PKCS1: formatPEM(` + -----BEGIN RSA PRIVATE KEY----- + MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu + KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm + o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k + TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7 + 9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy + v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs + /5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00 + -----END RSA PRIVATE KEY----- + `), + PKCS8: formatPEM(` + -----BEGIN PRIVATE KEY----- + MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqPfgaTEWEP3S9w0t + gsicURfo+nLW09/0KfOPinhYZ4ouzU+3xC4pSlEp8Ut9FgL0AgqNslNaK34Kq+NZ + jO9DAQIDAQABAkAgkuLEHLaqkWhLgNKagSajeobLS3rPT0Agm0f7k55FXVt743hw + Ngkp98bMNrzy9AQ1mJGbQZGrpr4c8ZAx3aRNAiEAoxK/MgGeeLui385KJ7ZOYktj + hLBNAB69fKwTZFsUNh0CIQEJQRpFCcydunv2bENcN/oBTRw39E8GNv2pIcNxZkcb + NQIgbYSzn3Py6AasNj6nEtCfB+i1p3F35TK/87DlPSrmAgkCIQDJLhFoj1gbwRbH + /bDRPrtlRUDDx44wHoEhSDRdy77eiQIgE6z/k6I+ChN1LLttwX0galITxmAYrOBh + BVl433tgTTQ= + -----END PRIVATE KEY----- + `), +}; + +test('pkcs8FromPKCS1', (t) => { + const der = pkcs8FromPKCS1(FIXTURES.PKCS1); + const pem = pemFromPKCS8(der); + t.is(pem, FIXTURES.PKCS8); +}); + +test('pkcs1FromPKCS8', (t) => { + const der = pkcs1FromPKCS8(FIXTURES.PKCS8); + const pem = pemFromPKCS1(der); + t.is(pem, FIXTURES.PKCS1); +}); diff --git a/test/utils/crypto.js b/test/utils/crypto.js index aaf11c1..62a0d5e 100644 --- a/test/utils/crypto.js +++ b/test/utils/crypto.js @@ -1,8 +1,7 @@ -import test from 'ava'; - -import { createECKey, createHS256JWK, parseAlgorithmIdentifier } from '../../lib/jwa.js'; -import { generateECDSA, importJWK, jwkFromPKCS8, pkcs8FromJWK } from '../../utils/crypto.js'; -import { derFromPEM, derFromPKCS8, formatPem, pemFromPKCS8 } from '../../utils/pkcs.js'; +import { createHS256JWK } from '../../lib/jwa.js'; +import { derFromPEM, derFromPKCS8, formatPEM, jwkFromPKCS8, jwkFromRSAPrivateKey, pemFromPKCS8, pkcs8FromJWK } from '../../utils/certificate.js'; +import { importJWK } from '../../utils/crypto.js'; +import test from '../tester.js'; test('importJWK()', async (t) => { const jwk = createHS256JWK({ k: 'AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow' }); @@ -50,8 +49,8 @@ test('jwkFromPKCS8() - rsa', async (t) => { t.is(jwk.alg, 'PS256'); }); -test('pkcs8FromJWK() - rsa', async (t) => { - const pem = `-----BEGIN PRIVATE KEY----- +test('pkcs8FromJWK() - RSA-PSS', async (t) => { + const pem = formatPEM(`-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDD0tPV/du2vftjvXj1t/gXTK39 sNBVrOAEb/jKzXae+Xa0H+3LhZaQIQNMfACiBSgIfZUvEGb+7TqXWQpoLoFR/R7MvGWcSk98JyrV tveD8ZmZYyItSY7m2hcasqAFiKyOouV5vzyRe87/lEyzzBpF3bQQ4IDaQu+K9Hj5fKuU6rrOeOhs @@ -74,7 +73,7 @@ test('pkcs8FromJWK() - rsa', async (t) => { 4qECgYEA0ZF6Vavz28+8wLO6SP3w8NmpHk7K9tGEvUfQ30SgDx4G7qPIgfPrbB4OP/E0qCfsIImi 3sCPpjvUMQdVVZyPOIMuB+rV3ZOxkrzxEUOrpOpR48FZbL7RN90yRQsAsrp9e4iv8QwB3VxLe7X0 TDqqnRyqrc/osGzuS2ZcHOKmCU8= - -----END PRIVATE KEY-----`; + -----END PRIVATE KEY-----`); const jwk = { kty: 'RSA', @@ -90,11 +89,11 @@ test('pkcs8FromJWK() - rsa', async (t) => { }; const pkcs8 = await pkcs8FromJWK(jwk, { name: 'RSA-PSS', hash: 'SHA-256' }); const result = pemFromPKCS8(pkcs8); - t.is(formatPem(pem), result); + t.is(pem, result); }); -test('jwkFromPKCS8() - auto select algorithm (rsa)', async (t) => { - const pem = `-----BEGIN PRIVATE KEY----- +test('pkcs8FromJWK() - RSA', async (t) => { + const pem = formatPEM(`-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDD0tPV/du2vftjvXj1t/gXTK39 sNBVrOAEb/jKzXae+Xa0H+3LhZaQIQNMfACiBSgIfZUvEGb+7TqXWQpoLoFR/R7MvGWcSk98JyrV tveD8ZmZYyItSY7m2hcasqAFiKyOouV5vzyRe87/lEyzzBpF3bQQ4IDaQu+K9Hj5fKuU6rrOeOhs @@ -117,57 +116,45 @@ test('jwkFromPKCS8() - auto select algorithm (rsa)', async (t) => { 4qECgYEA0ZF6Vavz28+8wLO6SP3w8NmpHk7K9tGEvUfQ30SgDx4G7qPIgfPrbB4OP/E0qCfsIImi 3sCPpjvUMQdVVZyPOIMuB+rV3ZOxkrzxEUOrpOpR48FZbL7RN90yRQsAsrp9e4iv8QwB3VxLe7X0 TDqqnRyqrc/osGzuS2ZcHOKmCU8= - -----END PRIVATE KEY-----`; + -----END PRIVATE KEY-----`); - const der = derFromPEM(pem); - const jwk = await jwkFromPKCS8(der); - t.deepEqual( - jwk, - { - kty: 'RSA', - alg: 'PS256', - n: 'w9LT1f3btr37Y7149bf4F0yt_bDQVazgBG_4ys12nvl2tB_ty4WWkCEDTHwAogUoCH2VLxBm_u06l1kKaC6BUf0ezLxlnEpPfCcq1bb3g_GZmWMiLUmO5toXGrKgBYisjqLleb88kXvO_5RMs8waRd20EOCA2kLvivR4-XyrlOq6znjobHZyXPlXQ0C0nB8bzKC2fVbbfqCvSeP7Ti8K-AhvJIQ5QVEXAWOoD3C6N3T1NPUlvunLV9nF-JpF8ejv9JpnU5mnK_77udCnUTJXhXmWDljwBIYTH8sMm07swYj1SGVT_FhQkijTG34tUvQPT3Qap3m8MB4V9_4bBAbdgQ', - e: 'AQAB', - d: 'BEI1P6nf6Zs7mJlyBDv-Pfl5rjL2cOqLy6TovvZVblMkCPpJyFuNIPDK2tK2i897ZaXfhPDBIKmllM2Hq6jZQKB110OAnTPDg0JxzMiIHPs32S1d_KilHjGff4Hjd4NXp1l1Dp8BUPOllorR2TYm2x6dcCGFw9lhTr8O03Qp4hjn84VjGIWADYCk83mgS4nRsnHkdiqYnWx1AjKlY51yEK6RcrDMi0Th2RXrrINoC35sVv-APt2rkoMGi52RwTEseA1KZGFrxjq61ReJif6p2VXEcvHeX6CWLx014LGk43z6Q28P6HgeEVEfIjyqCUea5Du_mYb_QsRSCosXLxBqwQ', - p: '9fn3QvWYjK1SPpJuzcdgigZ_Kyaw5lIeUm159jGCXa1HfG2DZ2ZAoCjIMVV7Lya25lTq28trcToqrnWZJOZg9xOTv0GVj1kC4EbvLnmDaaGwF_TUYDX2e62Q_vNb4eTH2Yl37mTrs79vYmYVNKKsmfcu-Xh5C3a3qzZMGDnuWlE', - q: 'y82qQ4tTcZAYLI_YojuClGu47w-CY0ahcuGYY_420L5bzNBf7hae7MVXBxseK9dQvuogm4V-LvmbrtBxo0-HfoTV5oBssUfHyFFcTpTrQ5FSWubDgWSu9__fzAXirrHZiT0YWpWUHzFCUViKBFQzIX4N7Vv6wY-UWALT9nU1VDE', - dp: 'OzzKvnZ1GZP4FZegVbBpYHQ2FgdIXP9zy_gPginkMnkzmRSqq7ElaSzJIZBrjSxuqcPTl8FCi88tTjyF-Cv_OCGf2FSMFyyhk6-hlHixHDRTO0G8D7uPM7PWEoA7JYi6VHpVxrTJSs2UnoblHnr6xE2SI4RO6mLZ0sLNypvQ-jE', - dq: 'ZyNHvTLvIZN4iGSrjz5qkM4LIwBIThFadxbv1fq6pt0O_BGf2o-cEdq0diYlGK64cEVwBwSBnSg4vzlBqRIAUejLjwEDAJyA4EE8Y5A9l04dzV7nJb5cRak6CrgXxay_mBJRFtaHxVlaZGxYPGSYE6UFS0-3EOmmevvDZQBf4qE', - qi: '0ZF6Vavz28-8wLO6SP3w8NmpHk7K9tGEvUfQ30SgDx4G7qPIgfPrbB4OP_E0qCfsIImi3sCPpjvUMQdVVZyPOIMuB-rV3ZOxkrzxEUOrpOpR48FZbL7RN90yRQsAsrp9e4iv8QwB3VxLe7X0TDqqnRyqrc_osGzuS2ZcHOKmCU8', - ext: true, - key_ops: [ - 'sign', - ], - }, - ); + const jwk = { + kty: 'RSA', + alg: 'PS256', + n: 'w9LT1f3btr37Y7149bf4F0yt_bDQVazgBG_4ys12nvl2tB_ty4WWkCEDTHwAogUoCH2VLxBm_u06l1kKaC6BUf0ezLxlnEpPfCcq1bb3g_GZmWMiLUmO5toXGrKgBYisjqLleb88kXvO_5RMs8waRd20EOCA2kLvivR4-XyrlOq6znjobHZyXPlXQ0C0nB8bzKC2fVbbfqCvSeP7Ti8K-AhvJIQ5QVEXAWOoD3C6N3T1NPUlvunLV9nF-JpF8ejv9JpnU5mnK_77udCnUTJXhXmWDljwBIYTH8sMm07swYj1SGVT_FhQkijTG34tUvQPT3Qap3m8MB4V9_4bBAbdgQ', + e: 'AQAB', + d: 'BEI1P6nf6Zs7mJlyBDv-Pfl5rjL2cOqLy6TovvZVblMkCPpJyFuNIPDK2tK2i897ZaXfhPDBIKmllM2Hq6jZQKB110OAnTPDg0JxzMiIHPs32S1d_KilHjGff4Hjd4NXp1l1Dp8BUPOllorR2TYm2x6dcCGFw9lhTr8O03Qp4hjn84VjGIWADYCk83mgS4nRsnHkdiqYnWx1AjKlY51yEK6RcrDMi0Th2RXrrINoC35sVv-APt2rkoMGi52RwTEseA1KZGFrxjq61ReJif6p2VXEcvHeX6CWLx014LGk43z6Q28P6HgeEVEfIjyqCUea5Du_mYb_QsRSCosXLxBqwQ', + p: '9fn3QvWYjK1SPpJuzcdgigZ_Kyaw5lIeUm159jGCXa1HfG2DZ2ZAoCjIMVV7Lya25lTq28trcToqrnWZJOZg9xOTv0GVj1kC4EbvLnmDaaGwF_TUYDX2e62Q_vNb4eTH2Yl37mTrs79vYmYVNKKsmfcu-Xh5C3a3qzZMGDnuWlE', + q: 'y82qQ4tTcZAYLI_YojuClGu47w-CY0ahcuGYY_420L5bzNBf7hae7MVXBxseK9dQvuogm4V-LvmbrtBxo0-HfoTV5oBssUfHyFFcTpTrQ5FSWubDgWSu9__fzAXirrHZiT0YWpWUHzFCUViKBFQzIX4N7Vv6wY-UWALT9nU1VDE', + dp: 'OzzKvnZ1GZP4FZegVbBpYHQ2FgdIXP9zy_gPginkMnkzmRSqq7ElaSzJIZBrjSxuqcPTl8FCi88tTjyF-Cv_OCGf2FSMFyyhk6-hlHixHDRTO0G8D7uPM7PWEoA7JYi6VHpVxrTJSs2UnoblHnr6xE2SI4RO6mLZ0sLNypvQ-jE', + dq: 'ZyNHvTLvIZN4iGSrjz5qkM4LIwBIThFadxbv1fq6pt0O_BGf2o-cEdq0diYlGK64cEVwBwSBnSg4vzlBqRIAUejLjwEDAJyA4EE8Y5A9l04dzV7nJb5cRak6CrgXxay_mBJRFtaHxVlaZGxYPGSYE6UFS0-3EOmmevvDZQBf4qE', + qi: '0ZF6Vavz28-8wLO6SP3w8NmpHk7K9tGEvUfQ30SgDx4G7qPIgfPrbB4OP_E0qCfsIImi3sCPpjvUMQdVVZyPOIMuB-rV3ZOxkrzxEUOrpOpR48FZbL7RN90yRQsAsrp9e4iv8QwB3VxLe7X0TDqqnRyqrc_osGzuS2ZcHOKmCU8', + }; + const pkcs8 = await pkcs8FromJWK(jwk, { name: 'RSA-PSS', hash: 'SHA-256' }); + const result = pemFromPKCS8(pkcs8); + t.is(pem, result); }); -test('generateEC Key', async (t) => { - const jwk = await generateECDSA(); - console.log(jwk); - const der = pkcs8FromJWK(jwk, parseAlgorithmIdentifier(jwk)); - console.log(der); +test('jwkFromRSAPrivateKey()', async (t) => { + const pem = `-----BEGIN RSA PRIVATE KEY----- + MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu + KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm + o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k + TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7 + 9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy + v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs + /5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00 + -----END RSA PRIVATE KEY-----`; + const der = derFromPEM(pem); + const jwk = await jwkFromRSAPrivateKey(der, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }); + t.is(jwk.kty, 'RSA'); + t.is(jwk.alg, 'RS256'); + t.is(jwk.d, 'IJLixBy2qpFoS4DSmoEmo3qGy0t6z09AIJtH-5OeRV1be-N4cDYJKffGzDa88vQENZiRm0GRq6a-HPGQMd2kTQ'); + t.is(jwk.dp, 'bYSzn3Py6AasNj6nEtCfB-i1p3F35TK_87DlPSrmAgk'); + t.is(jwk.dq, 'yS4RaI9YG8EWx_2w0T67ZUVAw8eOMB6BIUg0Xcu-3ok'); + t.is(jwk.e, 'AQAB'); + t.is(jwk.n, 'qPfgaTEWEP3S9w0tgsicURfo-nLW09_0KfOPinhYZ4ouzU-3xC4pSlEp8Ut9FgL0AgqNslNaK34Kq-NZjO9DAQ'); + t.is(jwk.p, 'oxK_MgGeeLui385KJ7ZOYktjhLBNAB69fKwTZFsUNh0'); + t.is(jwk.q, 'AQlBGkUJzJ26e_ZsQ1w3-gFNHDf0TwY2_akhw3FmRxs1'); + t.is(jwk.qi, 'E6z_k6I-ChN1LLttwX0galITxmAYrOBhBVl433tgTTQ'); }); - -// test('jwkFromPKCS1()', async (t) => { -// const pem = `-----BEGIN RSA PRIVATE KEY----- -// MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu -// KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm -// o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k -// TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7 -// 9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy -// v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs -// /5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00 -// -----END RSA PRIVATE KEY-----`; -// const jwk = await jwkFromPKCS1(pem, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }); -// t.is(jwk.kty, 'RSA'); -// t.is(jwk.alg, 'RS256'); -// t.is(jwk.d, 'IJLixBy2qpFoS4DSmoEmo3qGy0t6z09AIJtH-5OeRV1be-N4cDYJKffGzDa88vQENZiRm0GRq6a-HPGQMd2kTQ'); -// t.is(jwk.dp, 'bYSzn3Py6AasNj6nEtCfB-i1p3F35TK_87DlPSrmAgk'); -// t.is(jwk.dq, 'yS4RaI9YG8EWx_2w0T67ZUVAw8eOMB6BIUg0Xcu-3ok'); -// t.is(jwk.e, 'AQAB'); -// t.is(jwk.n, 'qPfgaTEWEP3S9w0tgsicURfo-nLW09_0KfOPinhYZ4ouzU-3xC4pSlEp8Ut9FgL0AgqNslNaK34Kq-NZjO9DAQ'); -// t.is(jwk.p, 'oxK_MgGeeLui385KJ7ZOYktjhLBNAB69fKwTZFsUNh0'); -// t.is(jwk.q, 'AQlBGkUJzJ26e_ZsQ1w3-gFNHDf0TwY2_akhw3FmRxs1'); -// t.is(jwk.qi, 'E6z_k6I-ChN1LLttwX0galITxmAYrOBhBVl433tgTTQ'); -// }); diff --git a/test/utils/pkcs.js b/test/utils/pkcs.js deleted file mode 100644 index df92d78..0000000 --- a/test/utils/pkcs.js +++ /dev/null @@ -1,18 +0,0 @@ -import test from 'ava'; - -import { encodeBase64AsString } from '../../utils/base64.js'; -import { pkcs8FromPKCS1 } from '../../utils/pkcs.js'; - -test('pkcs8FromPKCS1', (t) => { - const pem = `-----BEGIN RSA PRIVATE KEY----- - MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu - KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm - o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k - TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7 - 9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy - v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs - /5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00 - -----END RSA PRIVATE KEY-----`; - const pksc8 = pkcs8FromPKCS1(pem); - t.is(encodeBase64AsString(pksc8), 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqPfgaTEWEP3S9w0tgsicURfo+nLW09/0KfOPinhYZ4ouzU+3xC4pSlEp8Ut9FgL0AgqNslNaK34Kq+NZjO9DAQIDAQABAkAgkuLEHLaqkWhLgNKagSajeobLS3rPT0Agm0f7k55FXVt743hwNgkp98bMNrzy9AQ1mJGbQZGrpr4c8ZAx3aRNAiEAoxK/MgGeeLui385KJ7ZOYktjhLBNAB69fKwTZFsUNh0CIQEJQRpFCcydunv2bENcN/oBTRw39E8GNv2pIcNxZkcbNQIgbYSzn3Py6AasNj6nEtCfB+i1p3F35TK/87DlPSrmAgkCIQDJLhFoj1gbwRbH/bDRPrtlRUDDx44wHoEhSDRdy77eiQIgE6z/k6I+ChN1LLttwX0galITxmAYrOBhBVl433tgTTQ='); -}); diff --git a/utils/asn1/encoder.js b/utils/asn1/encoder.js index 4bdb11c..ffc0e43 100644 --- a/utils/asn1/encoder.js +++ b/utils/asn1/encoder.js @@ -254,14 +254,17 @@ export function encodeOctetString(entry) { } /** + * https://www.rfc-editor.org/rfc/rfc4055#section-2.1 * @param {string} oid - * @param {number[]} [params] + * @param {number[]|string|null} [params] DER or OID * @return {number[]} */ export function encodeAlgorithmIdentifer(oid, params) { return encodeSequence( encodeObjectIdentifier(oid), - params ?? encodeNull(), + typeof params === 'string' + ? encodeObjectIdentifier(oid) + : params ?? encodeNull(), // Supposed to optional, but made mandatory for RSA compatibility ); }