Skip to content

Commit

Permalink
test: wrap for deno testing
Browse files Browse the repository at this point in the history
  • Loading branch information
clshortfuse committed Feb 27, 2023
1 parent a513a65 commit 399fbbc
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 95 deletions.
3 changes: 1 addition & 2 deletions test/lib/ACMEAgent.js
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
3 changes: 1 addition & 2 deletions test/lib/jwe.js
Original file line number Diff line number Diff line change
@@ -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' };
Expand Down
3 changes: 1 addition & 2 deletions test/lib/jwk.js
Original file line number Diff line number Diff line change
@@ -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' };
Expand Down
86 changes: 84 additions & 2 deletions test/lib/jws.js
Original file line number Diff line number Diff line change
@@ -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}';
Expand Down Expand Up @@ -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));
});
2 changes: 1 addition & 1 deletion test/sanity.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import test from 'ava';
import test from './tester.js';

test('sync test', (t) => {
t.pass();
Expand Down
83 changes: 83 additions & 0 deletions test/tester.js
Original file line number Diff line number Diff line change
@@ -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/[email protected]/testing/asserts.ts');
/**
*
* @param {string} title
* @param {import('ava').ImplementationFn<any, any>} 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;
})();
5 changes: 2 additions & 3 deletions test/utils/asn1.js
Original file line number Diff line number Diff line change
@@ -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]);
Expand Down
3 changes: 1 addition & 2 deletions test/utils/base64.js
Original file line number Diff line number Diff line change
@@ -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();

Expand Down
40 changes: 40 additions & 0 deletions test/utils/certificate.js
Original file line number Diff line number Diff line change
@@ -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);
});
Loading

0 comments on commit 399fbbc

Please sign in to comment.